/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 adventure_map.cpp

	$Header: /heroes4/adventure_map.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "adventure_map.h"

#include <bitset>
#include <stack>
#include <stdlib.h>

#include "actor.h"
#include "abstract_adv_object.h"
#include "abstract_script_action.h"
#include "adv_actor_action.h"
#include "adv_ferry.h"
#include "adv_object_strings.h"
#include "adv_shipyard.h"
#include "adv_subterranean_gate.h"
#include "adv_teleporter_exit.h"
#include "adv_whirlpool.h"
#include "adventure_ai.h"
#include "adventure_events_base.h"
#include "adventure_events_look_trigger.h"
#include "adventure_events_mover.h"
#include "adventure_events_place.h"
#include "adventure_events_remove.h"
#include "adventure_events_set_action.h"
#include "adventure_events_set_owner.h"
#include "adventure_events_teleport.h"
#include "adventure_events_update.h"
#include "adventure_events_visiblity.h"
#include "adventure_frame.h"
#include "adventure_map_window.h"
#include "adventure_object_global_id.h"
#include "ai_importance.h"
#include "army.h"
#include "artifact.h"
#include "artifact_prop_single_spell.h"
#include "artifact_properties.h"
#include "artifact_slot.h"
#include "basic_dialog.h"
#include "bitmap_group.h"
#include "bitmap_group_cache.h"
#include "bitmap_layer_window.h"
#include "bitset_io.h"
#include "bool_array.h"
#include "bound_handler.h"
#include "caravan.h"
#include "carryover_data.h"
#include "compressed_filter.h"
#include "creature.h"
#include "creature_traits.h"
#include "default_artifact_properties.h"
#include "direction.h"
#include "elements_of.h"
#include "enum_operations.h"
#include "external_string.h"
#include "footprint.h"
#include "function_handler.h"
#include "game_time.h"
#include "gateway.h"
#include "global_event.h"
#include "hero.h"
#include "hero_portrait.h"
#include "memory_buffer.h"
#include "map_header.h"
#include "map_point_list.h"
#include "map_rect.h"
#include "map_renderer.h"
#include "map_segment_overlay.h"
#include "mini_map_window.h"
#include "music.h"
#include "object_registration.h"
#include "options.h"
#include "ownable_garrisonable_adv_object.h"
#include "path_arrow.h"
#include "pixel_24.h"
#include "player.h"
#include "player_color.h"
#include "progress_handler.h"
#include "random.h"
#include "random_names.h"
#include "replace_keywords.h"
#include "respawn_point.h"
#include "saved_combat.h"
#include "saved_game_header.h"
#include "script_targeting.h"
#include "selection_marker.h"
#include "spell_properties.h"
#include "square.h"
#include "stationary_adventure_object.h"
#include "streambuf_operators.h"
#include "timed_event.h"
#include "town.h"
#include "town_list.h"
#include "transition_calculator.h"
#include "transition_coordinates.h"
#include "transition_mask.h"
#include "transitions.h"

extern t_external_string const k_text_computer_player; // Name of computer players
extern t_external_string const k_default_loss_condition;
extern t_external_string const k_default_victory_condition;

// -------------------------------------------------------------------
// Unnamed namespace
// -------------------------------------------------------------------

namespace
{
	t_map_point_2d const k_edge_offset[2] = 
		{ t_map_point_2d(0,1), t_map_point_2d(1,0) };

	// -------------------------------------------------------------------
	// -------------------------------------------------------------------
	int get_tile_version_from_map_version( int map_version )
	{
		if ( map_version >= 26 )
			return 4;
		if ( map_version >= 19 )
			return 3;
		if ( map_version >= 8 )
			return 2;
		if ( map_version >= 6 )
			return 1;
		return 0;
	}

	// -------------------------------------------------------------------
	// -------------------------------------------------------------------
	int get_tile_version_from_saved_game_version( int saved_game_version )
	{
		if ( saved_game_version >= 36 )
			return 4;
		if ( saved_game_version >= 14 )
			return 3;
		if ( saved_game_version >= 3 )
			return 2;
		if ( saved_game_version >= 2 )
			return 1;
		return 0;
	}

	// -------------------------------------------------------------------
	// -------------------------------------------------------------------
	bool read_old_timed_event(
		std::streambuf &		stream,
		int						version,
		t_global_timed_event &	event )
	{
		int first_occurrence = get< t_uint16 >( stream );
		int recurrence_interval = get< t_uint16 >( stream );

		bool execution_flag = false;
		if ( version >= 1 )
		{
			get< bool >( stream ); // Discard "current player only" flag
			execution_flag = get< bool >( stream );
		}

		t_global_timed_event::t_script_ptr script_ptr =
			t_global_timed_event::t_script::reconstruct( stream, version );
		if ( script_ptr.get() == 0 )
			return false;

		event.set_first_occurrence( first_occurrence );
		event.set_recurrence_interval( recurrence_interval );
		event.set_script( script_ptr );

		return true;
	}

	// -------------------------------------------------------------------
	// Helper for t_adventure_map::read. Called if new artifacts are added
	// to the artifact list
	// type is the new artifact type
	// setting indicates the value m_used_artifacts[type] should have
	// -------------------------------------------------------------------
	void handle_used_artifacts( t_artifact_type type, bool & setting )
	{
		setting = false;
	}

	// -------------------------------------------------------------------
	// Helper for t_adventure_map::read_allowed_artifacts. Called if new 
	// artifacts are added to the artifact list
	// type is the new artifact type
	// setting indicates the value m_allowed_artifacts[type] should have
	// -------------------------------------------------------------------
	void handle_allowed_artifacts( t_artifact_type type, bool & setting )
	{
		setting = !is_cut( type ) && allowed_by_default( type );
	}

	// -------------------------------------------------------------------
	// Given a set of "visibility points" build a set of tile point for
	// which the visibility state differs from the map
	// -------------------------------------------------------------------
	t_adventure_map::t_tile_point_set build_visibility_difference_set(
		t_adventure_map const &		map,
		int							team_num,
		int							level,
		t_visibility_set const &	visibility_points )
	{
		typedef t_adventure_map::t_tile_point_set t_tile_point_set;

		t_tile_point_set result;

		t_visibility_set::const_iterator point_end = visibility_points.end();
		t_visibility_set::const_iterator point_iter = visibility_points.begin();
		for ( ; point_iter != point_end; ++point_iter )
		{
			assert( map.is_valid( *point_iter ) );
			t_adventure_tile const & tile = map.get_adv_tile( t_level_map_point_2d( *point_iter, level ) );

			if ( point_iter->get_visibility() != tile.get_visibility( team_num ) )
				result.insert( *point_iter );
		}

		return result;
	}

	// -------------------------------------------------------------------
	// Given a set of tiles for which visibility has changed build a set
	// of tiles which need to have their shroud transition recalculated
	// -------------------------------------------------------------------
	t_adventure_map::t_tile_point_set build_transition_test_set(
		t_adventure_map const &						map,
		t_adventure_map::t_tile_point_set const &	changed_tile_points )
	{
		typedef t_adventure_map::t_tile_point_set t_tile_point_set;

		t_tile_point_set result = changed_tile_points;

		t_tile_point_set::const_iterator tile_point_end = changed_tile_points.end();
		t_tile_point_set::const_iterator tile_point_iter = changed_tile_points.begin();
		for (	;
				tile_point_iter != tile_point_end;
				++tile_point_iter )
		{
			t_map_point_2d const & tile_point = *tile_point_iter;
			assert( map.is_valid( tile_point ) );

			t_direction direction;
			for (	direction = t_direction( 0 );
					direction < k_direction_count;
					enum_incr( direction ) )
			{
				t_map_point_2d adj_tile_point = tile_point + get_direction_offset( direction );
				if ( map.is_valid( adj_tile_point ) )
					result.insert( adj_tile_point );
			}
		}

		return result;
	}

	// --------------------------------------------------------
	// Get a bool array giving which creatures should be
	// respawned using the special "water respawning" rules
	// --------------------------------------------------------
	std::bitset< k_creature_type_count > const &get_respawn_as_water_creature()
	{
		static std::bitset< k_creature_type_count > k_respawn_as_water_creature;
		static bool initialized = false;

		if ( !initialized )
		{
			initialized = true;
			k_respawn_as_water_creature.reset();

			k_respawn_as_water_creature.set( k_sea_monster );
			k_respawn_as_water_creature.set( k_pirate );
			k_respawn_as_water_creature.set( k_water_elemental );
			k_respawn_as_water_creature.set( k_mermaid );
		}

		return k_respawn_as_water_creature;
	}

	// --------------------------------------------------------
	// Get a bool array giving which creatures can be
	// respawned on land
	// --------------------------------------------------------
	std::bitset< k_creature_type_count > const &get_respawn_as_land_creature()
	{
		static std::bitset< k_creature_type_count > k_respawn_as_land_creature;
		static bool initialized = false;

		if ( !initialized )
		{
			initialized = true;
			k_respawn_as_land_creature = ~get_respawn_as_water_creature();

			// water elemental is super-special -- can respawn on land OR water
			k_respawn_as_land_creature.set( k_water_elemental );
		}

		return k_respawn_as_land_creature;
	}

	// --------------------------------------------------------
	// --------------------------------------------------------
	void add_artifacts_to_carryover_data(
		t_creature const &		stack,
		t_artifact_set const &	mask,
		t_carryover_data &		data )
	{
		int count = stack.get_backpack_count();
		int num;
		for ( num = 0; num < count; ++num )
		{
			t_artifact const & artifact = stack.get_backpack_slot( num );
			if ( mask[ artifact.get_icon() ] )
				data.add( artifact );
		}
	}

	// --------------------------------------------------------
	// Remove all artifacts from the specified hero carryover
	// data which are not supposed to carryover
	// --------------------------------------------------------
	void strip_extra_artifacts(
		t_hero_carryover_data &	hero_data,
		t_artifact_set const &	mask )
	{
		t_artifact_slot slot;
		for ( slot = t_artifact_slot( 0 ); slot < k_artifact_slot_hero_count; enum_incr( slot ) )
		{
			t_artifact_type type = hero_data.get_artifact( slot ).get_icon();
			if ( type >= 0 && !mask[ type ] )
				hero_data.set_artifact( slot, t_artifact() );
		}

		typedef t_hero_carryover_data::t_backpack t_backpack;
		t_backpack new_backpack;

		if ( mask.any() )
		{
			t_backpack const & backpack = hero_data.get_backpack();
			t_backpack::const_iterator backpack_end = backpack.end();
			t_backpack::const_iterator backpack_iter = backpack.begin();
			for ( ; backpack_iter != backpack_end; ++backpack_iter )
			{
				t_artifact const & artifact = *backpack_iter;
				if ( mask[ artifact.get_icon() ] )
					new_backpack.push_back( artifact );
			}
		}

		hero_data.set_backpack( new_backpack );
	}

	// --------------------------------------------------------
	// --------------------------------------------------------
	void give_artifacts(
		t_creature_stack &					stack,
		std::vector< t_artifact > const &	artifacts )
	{
		std::vector< t_artifact >::const_iterator artifact_end = artifacts.end();
		std::vector< t_artifact >::const_iterator artifact_iter = artifacts.begin();
		for ( ; artifact_iter != artifact_end; ++artifact_iter )
			stack.add( *artifact_iter );
	}

	// --------------------------------------------------------
	// --------------------------------------------------------
	int compute_power( t_hero const & hero )
	{
		int result = 0;
		t_skill_type skill;
		for ( skill = t_skill_type( 0 ); skill < k_skill_type_count; enum_incr( skill ) )
			result += hero.get_skill( skill ) + 1;
		return result;
	}

	// --------------------------------------------------------
	// --------------------------------------------------------
	typedef t_player::t_creature_stack_set t_creature_stack_set;

	// --------------------------------------------------------
	// --------------------------------------------------------
	t_creature_stack_set build_creature_stack_set(
		t_adventure_map const &	map,
		int						player_number )
	{
		assert( player_number >= 0 && player_number < map.get_player_count() );

		t_creature_stack_set result;
		map.get_player( player_number ).build_creature_stack_set().swap( result );

		// Look at the towns for imprisoned heroes
		t_adventure_map::t_town_list::const_iterator town_end = map.get_towns_end();
		t_adventure_map::t_town_list::const_iterator town_iter = map.get_towns_begin();
		for ( ; town_iter != town_end; ++town_iter )
		{
			t_prisoner_list const & prisoners = ( *town_iter )->get_prisoner_list();
			t_prisoner_list::const_iterator prisoner_end = prisoners.end();
			t_prisoner_list::const_iterator prisoner_iter = prisoners.begin();
			for ( ; prisoner_iter != prisoner_end; ++prisoner_iter )
			{
				if ( prisoner_iter->owner_number == player_number )
					result.insert( prisoner_iter->hero );
			}
		}

		return result;
	}

	// --------------------------------------------------------
	// --------------------------------------------------------
	void distribute_artifacts(
		t_carryover_data &	carryover_data,
		t_adventure_map &	map )
	{
		std::vector< t_artifact > const & artifacts =
			carryover_data.retrieve_artifacts( map.get_carry_in_artifacts() );
		if ( artifacts.empty() )
			return;

		// Carryover artifacts go to the human player, so find the human
		int player_number;
		for ( player_number = 0; true; ++player_number )
		{
			assert( player_number < map.get_player_count() );
			if ( !map.get_player( player_number ).is_computer() )
				break;
		}

		// Get a set of all of the creature stacks belonging to this player
		t_creature_stack_set const & stacks = build_creature_stack_set( map, player_number );

		if ( stacks.empty() )
		{
			// Uh-oh, nobody to give these to
			return;
		}

		// First try to find a hero
		t_hero * best_hero_ptr = 0;
		int best_hero_power = 0;

		t_creature_stack_set::const_iterator stack_end = stacks.end();
		t_creature_stack_set::const_iterator stack_iter = stacks.begin();
		for ( ; stack_iter != stack_end; ++stack_iter )
		{
			t_hero * hero_ptr = ( *stack_iter )->get_hero();
			if ( hero_ptr == 0 )
				continue;

			int hero_power = compute_power( *hero_ptr );
			if ( best_hero_ptr == 0 || hero_power > best_hero_power )
			{
				best_hero_ptr = hero_ptr;
				best_hero_power = hero_power;
			}
		}

		if ( best_hero_ptr != 0 )
		{
			give_artifacts( *best_hero_ptr, artifacts );
			return;
		}

		// Unable to find a hero, find a creature
		t_creature * best_creature_ptr = 0;
		int best_creature_exp = 0;

		for ( stack_iter = stacks.begin(); stack_iter != stack_end; ++stack_iter )
		{
			t_creature * creature_ptr = ( *stack_iter )->get_creature();
			if ( creature_ptr == 0 )
				continue;

			int creature_exp = creature_ptr->get_experience_value();
			if ( best_creature_ptr == 0 || creature_exp > best_creature_exp )
			{
				best_creature_ptr = creature_ptr;
				best_creature_exp = creature_exp;
			}
		}

		if ( best_creature_ptr != 0 )
			give_artifacts( *best_creature_ptr, artifacts );
	}

	// -------------------------------------------------------------------
	// Mark the given point as impassable
	// -------------------------------------------------------------------
	 void mark_cell_as_impassable( t_adventure_map &    map,
								   t_adv_map_point		point, 
								   t_adventure_tile &   tile ) // = map[point]
	{
		assert( &tile == &map[point] );

		int						  edge;

		for (edge = 0; edge < 2; ++edge)
		{
			tile.change_edge_block_count( edge == 1, point.on_bridge, 1 );
			t_level_map_point_2d next_point = point + k_edge_offset[edge];
			if (map.is_valid( next_point ))
			{
				t_adventure_tile &next_tile = map.get_adv_tile( next_point );
				next_tile.change_edge_block_count( edge == 1, point.on_bridge, 1 );
			}
		}

		if ( !point.on_bridge )
		{
			tile.increment_block();
		}
		else
		{
			assert( false ); // Tiles are not set up to handle permanently blocking objects on bridges
		}
	}

	// -------------------------------------------------------------------
	// Mark all lava rivers as impassable
	// -------------------------------------------------------------------
	void mark_lava_rivers( t_adventure_map & map )
	{
		t_adv_map_point point;
		point.on_bridge = false;

		for ( point.level = 0; point.level < map.get_num_levels(); point.level++ )
		{
			for ( point.row = 0; point.row < map.get_size(); point.row++ )
			{
				for ( point.column = 0; point.column < map.get_size(); point.column++ )
				{
					if ( map.is_valid( point ) )
					{
						t_adventure_tile & tile = map.get_adv_tile( point );

						if ( tile.get_terrain() == k_terrain_lava_river )
						{
							mark_cell_as_impassable( map, point, tile );
						}
					}
				}
			}
		}
	}
} // unnamed namespace

// -------------------------------------------------------------------------------
// calculates the scouting rect for a given point and radius
// -------------------------------------------------------------------------------
t_map_rect_2d get_visibility_rect(
	t_map_point_2d const &	center, // In half cell units
	int						radius,
	int						map_size )
{
	int half_cell_radius = radius * 2;

	// Compute the visibility rect
	t_map_rect_2d	result(
						( center.row - half_cell_radius ) / 2,
						( center.column - half_cell_radius ) / 2,
						( center.row + half_cell_radius + 1 ) / 2 + 1,
						( center.column + half_cell_radius + 1 ) / 2 + 1 );

	// Clip the result to the map
	t_map_rect_2d map_rect( 0, 0, map_size, map_size );

	assert( intersect( result, map_rect ) );
	return intersection( result, map_rect );
}

// -------------------------------------------------------------------
// determine whether a trigger object blocks an army
// -------------------------------------------------------------------
bool t_adventure_tile::blocks_army( t_creature_array const&		army,
								    t_adventure_map const&		map,
									bool					    on_bridge,
									t_path_search_type			path_type ) const
{
	int i;

	if ( (path_type == k_path_search_ai_value) 
		|| (path_type == k_path_search_ai_danger) 
		|| (path_type == k_path_search_ai_guard_danger) )
	{
		if (!is_trigger( on_bridge ) && is_blocked( on_bridge ))
			return true;
	}
	else
	{
		if (is_blocked( on_bridge ))
			return true;
		if (!is_trigger( on_bridge ))
			return false;
	}

	t_adventure_object const* object;

	for (i = 0; i < m_trigger_objects[on_bridge].size(); ++i)
	{
		object = &map.get_adv_object( m_trigger_objects[on_bridge][i] );
		if (object->blocks_army( army, path_type ))
			return true;
	}
	return false;
}


// -------------------------------------------------------------------
// adventure map class.  Currently very basic.
// -------------------------------------------------------------------
t_adventure_map::t_adventure_map() 
   : t_abstract_adventure_map(), m_event_playback( m_event_buffer ),
	  m_replay_terrain_map(8000), m_replay_shroud_transition_map(2000)
{
	// We assume initialization will be handled by a subsequent 
	// "read" or "read_from_map" operation.
	initialize();

}


t_adventure_map::t_adventure_map( int size, int levels, int seed, t_difficulty player_difficulty, 
								  int turn_duration, bool guards_move )
	: t_abstract_adventure_map(), m_event_playback( m_event_buffer ), 
	  m_replay_terrain_map(8000), m_replay_shroud_transition_map(2000)
{
	initialize();

	// Initialize ourselves and our base class
	initialize( size, levels, seed, player_difficulty, turn_duration );
}


// ----------------------------------------------------------
// ----------------------------------------------------------
t_adventure_map::~t_adventure_map()
{
	// Give all objects one last chance to clean up
	int id = m_first_object_id;

	while (id != k_invalid_object_id)
	{
		m_objects[id].object->on_adventure_map_destruction();
		id = m_objects[id].next;
	}
}

void t_adventure_map::initialize()
{
	m_replay_event_state_map = new t_adventure_object_cache_manager;
	m_winning_team = -1;
	m_victory_timer = -1;
	m_cheater = false;
	m_is_replay_map = false;
	m_event_tick	= 0;
	m_in_neutral_turn = false;
	m_danger_marked = false;
	m_is_replay_map = false;
	memset( m_spell_count, 0, sizeof( m_spell_count ));
}

// ----------------------------------------------------------
// return the % of map explored by a team
// returns a value between 0.0f and 1.0f
// ----------------------------------------------------------
float t_adventure_map::get_percent_explored( int team_num )	const
{
	// fill with grass.
	t_level_map_point_2d point;
	int explored = 0;
	int total_tiles = 0;


	point.level = get_num_levels();
	while (point.level--)
	{
		point.row = get_size();
		while (point.row--)
		{
			point.column = m_map.get_row_end( point.row );
			while (point.column-- > m_map.get_row_start( point.row ))
			{
				++total_tiles;

				t_tile_visibility visibility;
				visibility = m_map.get( point ).get_visibility( team_num );
				if( visibility != k_tile_never_explored )
					++explored;
			}
		}
	}

	return ( (float)explored / (float)total_tiles );
}

// ----------------------------------------------------------
// Used by parameterized constructor and the "read" routines
// ----------------------------------------------------------
void t_adventure_map::initialize( int size, int levels, int seed, t_difficulty player_difficulty, int turn_duration_index )
{
	// Initialize our base class appropriatly
	t_params params;
	params.size = size;
	params.levels = levels;
	params.terrain_random_seed = seed;
	init( params );

	// Initialize our terrain map
	m_map.initialize( size, levels, k_adventure_tile_height );

	// Intialize miscellanious member values
	m_map_window = 0;
	m_first_object_id	= k_invalid_object_id;
	m_last_object_id	= k_invalid_object_id;
	m_first_free_id		= 0;
	m_single_use_object_count = 0;
	m_record_replay_events = false;
	m_vertex_map.initialize( size, levels, k_adventure_tile_height );
	m_standard_victory_condition = true;
	m_game_over = false;

	m_current_player_number = 0;
	m_player_number = 0;

	m_total_map_income = 0;
	m_towns.clear();

	m_next_month_creature = k_peasant;
	
	// initialize our obelisk list
    for (t_obelisk_color color = k_obelisk_gold; color < k_obelisk_color_count; enum_incr( color ))
    {
        m_obelisk_count[ color ] = 0;

        // These may be read from the map (editable properties), if necessary.
        set_obelisk_artifact_position( color,  t_level_map_point_2d (-1, -1, 0) );
    }
	m_player_difficulty = player_difficulty;
	m_turn_duration_index = turn_duration_index;
	m_adventure_ai_ptr.reset( new t_adventure_ai( *this ) );
	m_event_playback.attach(this);

	m_subterranean_gates.clear();
	m_ferries.clear();
	m_ferries_by_group.clear();
	m_ferry_groups_calculated = true; // Initially no ferries, so a blank m_ferries_by_group is correct
	m_gateways.clear();
	m_gateways.resize( k_gateway_type_count );
	m_teleporter_exits.clear();
	m_teleporter_exits.resize( k_gateway_type_count );
    m_completed_obelisk_flags.reset();
    m_obelisk_placed_treasure_flags.reset();

	m_used_random_names.clear();

	m_artifact_ai_importance_map.clear();
	m_parchment_ai_importance_map.clear();
	m_scroll_ai_importance_map.clear();
}


// ----------------------------------------------------
// ----------------------------------------------------
void t_adventure_map::add_respawn_point( t_respawn_point const & point )
{
	m_respawn_point_list.push_back(point);
}

// ----------------------------------------------------
// ----------------------------------------------------
t_adventure_event_list const & t_adventure_map::get_events_list()
{
	return m_event_buffer;
}

// ----------------------------------------------------
// ----------------------------------------------------
t_adventure_ai const& t_adventure_map::get_ai() const
{
	return *m_adventure_ai_ptr;
}

// ----------------------------------------------------
// ----------------------------------------------------
t_adventure_ai& t_adventure_map::get_ai()
{
	return *m_adventure_ai_ptr;
}

// -------------------------------------------------------------------
// adventure map class.  Currently very basic.
// -------------------------------------------------------------------
void t_adventure_map::attach( t_adventure_map_window* window )
{
	m_map_window = window;
}

// -------------------------------------------------------------------
// adventure map class.  Currently very basic.
// -------------------------------------------------------------------
t_adventure_map_window* t_adventure_map::get_map_window() const
{
	return m_map_window;
}

// -------------------------------------------------------------------
// adventure map class.  Currently very basic.
// -------------------------------------------------------------------
const t_abstract_adventure_map::t_tile& t_adventure_map::get_tile( t_level_map_point_2d point ) const
{
	return m_map.get( point );
}

t_abstract_adventure_map::t_tile & t_adventure_map::get_tile( t_level_map_point_2d point )
{
	return m_map.get( point );
}

const t_abstract_adventure_map::t_tile_vertex& t_adventure_map::get_tile_vertex( t_level_map_point_2d point ) const
{
	return m_vertex_map.get( point );
}

t_abstract_adventure_map::t_tile_vertex& t_adventure_map::get_tile_vertex( t_level_map_point_2d point )
{
	return m_vertex_map.get( point );
}

t_abstract_adventure_map::t_const_tile_vertex_quad t_adventure_map::get_tile_vertex_quad( t_level_map_point_2d point ) const
{
	t_tile_vertex const & left = m_vertex_map.get( t_level_map_point_2d( point.row + 1, point.column, point.level ) );
	t_tile_vertex const & top = m_vertex_map.get( point );
	t_tile_vertex const & right = m_vertex_map.get( t_level_map_point_2d( point.row, point.column + 1, point.level ) );
	t_tile_vertex const & bottom = m_vertex_map.get( t_level_map_point_2d( point.row + 1, point.column + 1, point.level ) );
	return t_const_tile_vertex_quad( left, top, right, bottom );
}

t_abstract_adventure_map::t_tile_vertex_quad t_adventure_map::get_tile_vertex_quad( t_level_map_point_2d point )
{
	t_tile_vertex & left = m_vertex_map.get( t_level_map_point_2d( point.row + 1, point.column, point.level ) );
	t_tile_vertex & top = m_vertex_map.get( point );
	t_tile_vertex & right = m_vertex_map.get( t_level_map_point_2d( point.row, point.column + 1, point.level ) );
	t_tile_vertex & bottom = m_vertex_map.get( t_level_map_point_2d( point.row + 1, point.column + 1, point.level ) );
	return t_tile_vertex_quad( left, top, right, bottom );
}

// -------------------------------------------------------------------
// adventure map class.
// place object on map.
// -------------------------------------------------------------------
t_adventure_map_object_id t_adventure_map::place_object( t_adventure_object* object, t_adv_map_point point )
{
	// find the next free id.
	int id = m_first_free_id;

	assert( id <= m_objects.size() );

	if (id == m_objects.size() )
	{  
		// id is at the end of the list.
		m_objects.push_back( t_object_node() );
		m_first_free_id++;
	}
	else
	{
		// update head of free list.
		assert( m_objects[id].object == 0 );
		assert( m_objects[id].next != k_invalid_object_id );
		m_first_free_id = m_objects[id].next;
	}

	m_objects[id].next		= k_invalid_object_id;
	m_objects[id].last		= m_last_object_id;
	m_objects[id].object	= object;
		
	if (m_first_object_id == k_invalid_object_id)
		m_first_object_id = id;

	if (m_last_object_id != k_invalid_object_id)
		m_objects[m_last_object_id].next = id;

	m_last_object_id = id;

	on_object_placed( id );

	t_map_renderer::on_adv_object_placed( *this, id );
	t_mini_map_renderer::on_adv_object_placed( *this, id );

	////////////////////////////////////////////////////////////
	// functionality
	
	record_placement_event( object, point );
	
	assign_global_id( object , object->get_global_id());
	
	////////////////////////////////////////////////////////////
	return id;
}

// -------------------------------------------------------------------
// adventure map class.  Currently very basic.
// -------------------------------------------------------------------
void t_adventure_map::remove_object( t_adventure_object* object )
{
	int                  id = object->get_map_id();
	t_level_map_point_2d point = object->get_position();

	////////////////////////////////////////////////////////////
	// functionality

	record_remove_event(object);

	assign_global_id( NULL, object->get_global_id());

	////////////////////////////////////////////////////////////

	assert( id != k_invalid_object_id );
	assert( m_objects[id].object.get() == object );
	on_removing_object( id );
	t_map_renderer::on_removing_adv_object( *this, id );
	t_mini_map_renderer::on_removing_adv_object( *this, id );

	// unlink node from list of objects.
	t_object_node& node = m_objects[id];

	if (node.next == k_invalid_object_id)
		m_last_object_id = node.last;
	else
		m_objects[node.next].last = node.last;
	if (node.last == k_invalid_object_id)
		m_first_object_id = node.next;
	else
	{
		assert( m_objects[node.last].object != 0 );
		m_objects[node.last].next = node.next;
	}

	// link node to list of free objects.
	node.next = m_first_free_id;
	node.last = k_invalid_object_id;
	m_first_free_id = id;

	t_map_renderer::on_adv_object_removed( *this, *object, point );
	object->on_removed();
	node.object = 0;
}

// -------------------------------------------------------------------
// remove_object : removes the object using the global id, makes it easier for the
// caller, in that they dont need to know what type of adv_object it is as long as
// it has a global id.
// -------------------------------------------------------------------
void t_adventure_map::remove_object( t_adventure_global_id const & object_gid )
{
	//PDL::
	//	-search for the adv object within the map.

	//adv_obj can be either army or statationary type.

	if (m_global_id_map.find(object_gid) == m_global_id_map.end())
		return;

	m_global_id_map[object_gid]->destroy();
}

// -------------------------------------------------------------------
// adventure map class.  Currently very basic.
// -------------------------------------------------------------------
namespace {
	struct t_object_position
	{
		t_adventure_object_ptr object;
		t_level_map_point_2d   point;

		t_object_position( t_adventure_object* obj, t_level_map_point_2d const& pt )
		{
			object = obj;
			point = pt;
		}
	};

	const int k_object_progress_value = 500000;

	bool read_objects( std::streambuf& buffer,
					   std::list<t_object_position>& objects,
					   t_progress_handler* handler,
					   int version )
	{
		int					 count = get<t_uint32>( buffer );
		t_level_map_point_2d point;
		bool				 is_army;
		std::string			 model_name;
		int					 increment;

		handler->increment( sizeof( t_uint32 ) );
		if (count > 0)
			increment = k_object_progress_value / count;
		while (count--)
		{
			t_adventure_object_ptr object;

			point.row		= get<t_uint32>(buffer);
			point.column	= get<t_uint32>(buffer);
			point.level		= get<t_uint32>(buffer);
			is_army			= get<t_uint8>(buffer) != 0;
			handler->increment( increment );
			if ( !is_army )
			{
				if (!read_string16( buffer, model_name ))
					return false;

				t_counted_ptr<t_stationary_adventure_object> stationary_object_ptr;
				
				stationary_object_ptr = create_adv_object( model_name );
				if ( version >= 14 )
				{
					t_qualified_adv_object_type in_file_type;
				
					if ( !read( buffer, in_file_type ) )
						return false;
	
					if ( stationary_object_ptr->get_type() != in_file_type )
						return false;
				}
				object = stationary_object_ptr.get();
			}
			else
			{
				if ( version < 5 )
				{
					// Read and dump the old model name and version number
					if ( !read_string16( buffer, model_name ) )
						return false;
					if ( version >= 4 )
						get< t_uint16 >( buffer );
					continue;
				}

				object = new t_army;
			}
			if ( version >= 4 && !object->read_from_map( buffer, handler ) )
				return false;
			objects.push_back( t_object_position( object, point ) );
		}
		return true;
	}

};

// -------------------------------------------------------------------
// adventure map class.  Currently very basic.
// -------------------------------------------------------------------
void t_adventure_map::read_heights( std::streambuf& buffer, t_progress_handler* handler )
{
	// Read the cell vertex data
	int                           size = m_map.get_size();
	int                           levels = m_map.get_levels();
	t_isometric_vertex_map< int > vertex_height_map( size, 
		                                             levels,
													 k_adventure_tile_height );
	t_level_map_point_2d          point;

	for ( point.level = 0; point.level < levels; ++point.level )
	{
		for ( point.row = 0; point.row < size + 1; ++point.row )
		{
			int row_start = get_vertex_row_start( point.row );
			int row_end = get_vertex_row_end( point.row );

			for ( point.column = row_start; point.column < row_end; ++point.column )
				vertex_height_map.get( point ) = get< t_uint8 >( buffer );
			handler->increment( row_end - row_start );
		}
	}

	apply_height_map( vertex_height_map );

	// Run through all of the tile vertices setting the height
	t_level_map_point_2d		vertex_point;
	t_adventure_tile_vertex*	vertex;

	for ( vertex_point.level = 0; vertex_point.level < levels; ++vertex_point.level )
	{
		for ( vertex_point.row = 0; vertex_point.row < size + 1; ++vertex_point.row )
		{
			int row_start = get_vertex_row_start( vertex_point.row );
			int row_end = get_vertex_row_end( vertex_point.row );

			for ( vertex_point.column = row_start; vertex_point.column < row_end; ++vertex_point.column )
			{
				assert( m_vertex_map.is_valid( vertex_point ) );
				vertex = &m_vertex_map.get( vertex_point );
				vertex->set_bridge_height( vertex->get_height() );
			}
		}
	}
}

void t_adventure_map::add_player( t_player_ptr new_player_ptr )
{
	m_players.push_back( new_player_ptr );
}

static void set_alignment( t_player_setup const& setup, t_player& player, 
						   t_map_header const& header )
{
	if (setup.alignment != k_town_none)
	{
		player.set_alignment( setup.alignment );
		return;
	}

	t_town_type_set const&	available_alignments = header.available_alignments[setup.color];
	int						count = available_alignments.count();

	if (count == 0)
	{
		player.set_alignment( random( k_town_type_count ) );
		return;
	}

	t_town_type alignment;

	count = random( count );
	for (alignment = t_town_type(0); alignment < k_town_type_count; enum_incr(alignment))
	{
		if (available_alignments[alignment])
		{
			if (--count < 0)
				break;
		}
	}
	player.set_alignment( alignment );
}

// -------------------------------------------------------------------
// adventure map class
// -------------------------------------------------------------------
bool t_adventure_map::set_players( t_map_header const &		header,
								   t_player_setup const*    player_setup )
{
    if (player_setup == NULL)
        return true;

	t_player_list  new_players;
	int			   i;

	for (i = 0; i < header.players; i++)
	{
		t_player_setup const& setup = player_setup[i];
		if (setup.human)
		{
			t_player_ptr new_player_ptr( new t_player( false, header.team_id[ setup.color ] ) );

			new_player_ptr->set_color( setup.color );
			new_player_ptr->set_name( setup.name );
			set_alignment( setup, *new_player_ptr, header );
			new_players.push_back( new_player_ptr );
		}
	}

	for (i = 0; i < header.players; i++)
	{
		t_player_setup const& setup = player_setup[i];
		if (!setup.human)
		{
			t_player_ptr new_player_ptr( new t_player( true, header.team_id[ setup.color ] ) );

			new_player_ptr->set_color( setup.color );
			new_player_ptr->set_name( setup.name );
			set_alignment( setup, *new_player_ptr, header );
			new_players.push_back( new_player_ptr );
		}
	}

	m_players.swap( new_players );
	initialize_players( m_player_difficulty );
	create_shroud_transition_maps();
	m_current_player_number = 0;
	m_player_number = 0;

	return true;
}

// -------------------------------------------------------------------
// adventure map class
// -------------------------------------------------------------------
void t_adventure_map::read_tile( std::streambuf& buffer,
		                         t_adventure_tile& tile,
						         t_level_map_point_2d const& point,
						         t_map_header const& header,
								 t_progress_handler* handler )
{
	assert( header.version >= 3 ); // Can't handle previous versions anymore

	tile.read( buffer, get_tile_version_from_map_version( header.version ) );
}

// -------------------------------------------------------------------
// adventure map class
// -------------------------------------------------------------------
bool t_adventure_map::read_timed_events_from_map( std::streambuf& stream,
												  t_map_header const& header )
{
	assert( header.version >= 11 );

	int count = get< t_uint16 >( stream );
	t_timed_event_list new_timed_events;
	new_timed_events.reserve( count );

	while ( count > 0 )
	{
		t_timed_event_ptr new_event_ptr( new t_timed_event );
		if ( !new_event_ptr->read_from_map( stream ) )
			return false;

		new_timed_events.push_back( new_event_ptr );

		--count;
	}

	m_timed_events.swap( new_timed_events );
	return true;
}

// -------------------------------------------------------------------
// adventure map class
// -------------------------------------------------------------------
bool t_adventure_map::read_triggerable_events_from_map( std::streambuf& stream,
														t_map_header const& header )
{
	assert( header.version >= 11 );

	int count = get< t_uint16 >( stream );
	t_triggerable_event_list new_triggerable_events;
	new_triggerable_events.reserve( count );

	while ( count > 0 )
	{
		t_triggerable_event_ptr new_event_ptr( new t_triggerable_event );
		if ( !new_event_ptr->read_from_map( stream ) ) 
			return false;

		new_triggerable_events.push_back( new_event_ptr );

		--count;
	}

	m_triggerable_events.swap( new_triggerable_events );
	return true;
}

// -------------------------------------------------------------------
// adventure map class
// -------------------------------------------------------------------
bool t_adventure_map::read_continuous_events_from_map( std::streambuf& stream,
													   t_map_header const& header )
{
	assert( header.version >= 11 );

	int count = get< t_uint16 >( stream );
	t_continuous_event_list new_continuous_events;
	new_continuous_events.reserve( count );

	while ( count > 0 )
	{
		t_continuous_event_ptr new_event_ptr( new t_continuous_event );
		if ( !new_event_ptr->read_from_map( stream ) ) 
			return false;

		new_continuous_events.push_back( new_event_ptr );

		--count;
	}

	m_continuous_events.swap( new_continuous_events );
	return true;
}

// -------------------------------------------------------------------
// adventure map class
// -------------------------------------------------------------------
bool t_adventure_map::read_placed_events_from_map( std::streambuf& stream,
												   t_map_header const& header )
{
	if ( header.version < 12 )
		return true;

	int count = get< t_uint16 >( stream );
	t_placed_event_list new_placed_events;
	new_placed_events.reserve( count );

	while ( count > 0 )
	{
		t_placed_event_ptr new_event_ptr( new t_placed_event );
		if ( !new_event_ptr->read_from_map( stream ) )
			return false;

		new_placed_events.push_back( new_event_ptr );

		--count;
	}

	m_placed_events.swap( new_placed_events );
	return true;
}

// -------------------------------------------------------------------
// adventure map class
// -------------------------------------------------------------------
bool t_adventure_map::read_obelisk_data_from_map( std::streambuf& stream,
											      t_map_header const& header )
{
	for ( t_obelisk_color color = t_obelisk_color(0); color < k_obelisk_color_count; enum_incr( color ) )
	{
		if ( !m_obelisk_data[ color ].read_from_map( color, stream, header.version ) )
			return false;
	}

	return true;
}

// -------------------------------------------------------------------
// adventure map class
// -------------------------------------------------------------------
bool t_adventure_map::read_player_data_body_from_map( std::streambuf& stream,
													  t_map_header const& header )
{
	if ( header.version < 16 )
		return true;

	int count = header.players;

	while ( count )
	{
		t_player_color color = t_player_color( get< t_uint8 >( stream ) );

		int index = 0;
		while ( index < m_players.size() && m_players[ index ]->get_color() != color )
			index++;

		if ( index >= m_players.size() )
			return false;

		if ( get< t_uint8 >( stream ) )
		{
			m_players[index]->set_max_level( get< t_uint16 >( stream ) );
		}
		else
		{
			m_players[index]->clear_max_level();
		}

		if ( header.version < 23 )
		{
			m_players[index]->set_ai_importance( k_ai_importance_normal );
		}
		else
		{
			m_players[index]->set_ai_importance( t_ai_importance( get< t_uint8 >( stream ) ) );
		}

		count--;
	}

	return true;
}

// -------------------------------------------------------------------
// -------------------------------------------------------------------
bool t_adventure_map::read_allowed_artifacts_from_map( std::streambuf & stream, t_map_header const& header )
{
	bool allowed_artifacts_custom_flag;

	if ( header.version < 18 )
	{
		allowed_artifacts_custom_flag = false;
	}
	else
	{
		allowed_artifacts_custom_flag = ( get< t_uint8 >( stream ) != 0 );
	}


	if ( allowed_artifacts_custom_flag )
	{
		if ( !m_allowed_artifacts.read( stream, function_2_handler( handle_allowed_artifacts ) ) )
			return false;
	}
	else
	{
		// Default
		for ( t_artifact_type type = t_artifact_type(0); type < k_artifact_type_count; enum_incr( type ) )
		{
			m_allowed_artifacts.set( type, !is_cut( type ) && allowed_by_default( type ) );
		}
	}

	return true;
}

// -------------------------------------------------------------------
// -------------------------------------------------------------------
bool t_adventure_map::read_allowed_heroes_from_map( std::streambuf & stream, t_map_header const& header )
{
	bool allowed_heroes_custom_flag;

	if ( header.version < 18 )
	{
		allowed_heroes_custom_flag = false;
	}
	else
	{
		allowed_heroes_custom_flag = ( get< t_uint8 >( stream ) != 0 );
	}

	if ( allowed_heroes_custom_flag )
	{
		if ( !m_allowed_heroes.read( stream ) )
			return false;
	}
	else
	{
		m_allowed_heroes.clear();
	}

	// Expand on default or if new heroes have been added since the write
	if ( m_allowed_heroes.size() < get_hero_portrait_count() )
	{
		m_allowed_heroes.reserve( get_hero_portrait_count() );

		while ( m_allowed_heroes.size() < get_hero_portrait_count() )
		{
			m_allowed_heroes.push_back( hero_allowed_by_default( m_allowed_heroes.size() ) );
		}
	}
	else if ( m_allowed_heroes.size() > get_hero_portrait_count() )
	{
		return false;
	}

	// Make sure m_default_heroes never returns a disallowed hero
	m_default_heroes.set_allowed_portrait_list( m_allowed_heroes );

	return true;
}

// -------------------------------------------------------------------
// -------------------------------------------------------------------
bool t_adventure_map::read_allowed_skills_from_map( std::streambuf & stream, t_map_header const& header )
{
	bool allowed_skills_custom_flag;
	if ( header.version < 18 )
	{
		allowed_skills_custom_flag = false;
	}
	else
	{
		allowed_skills_custom_flag = ( get< t_uint8 >( stream ) != 0 );
	}

	if ( allowed_skills_custom_flag )
	{
		if ( !m_allowed_skills.read( stream ) )
			return false;
	}
	else
	{
		// Default: all skills
		m_allowed_skills = ~t_skill_set();
	}

	return true;
}

// -------------------------------------------------------------------
// -------------------------------------------------------------------
bool t_adventure_map::read_allowed_spells_from_map( std::streambuf & stream, t_map_header const& header )
{
	bool allowed_spells_custom_flag;

	if ( header.version < 18 )
	{
		allowed_spells_custom_flag = false;
	}
	else
	{
		allowed_spells_custom_flag = ( get< t_uint8 >( stream ) != 0 );
	}

	int index;
	if ( allowed_spells_custom_flag )
	{
		// Note: must handle addition of new spells correctly 
		// (default them to allowed)
		t_uint16 size = get< t_uint16 >( stream );

		if ( size > m_allowed_spells.size() )
			return false;

		t_uint16 bytes = ( size + 7 ) / 8;

		t_owned_array< unsigned char > buffer( new unsigned char[bytes] );

		if ( !::read( stream, buffer.get(), bytes ) )
			return false;

		for ( index = 0; index < size; index++ )
		{
			m_allowed_spells.set( index, ( buffer[ index / 8 ] >> ( index % 8 ) ) & 0x1 );
		}
	}
	else
	{
		index = 0;
	}

	// Default new spells, or all if no custom spells
	for ( ; index < m_allowed_spells.size(); index++ )
	{
		m_allowed_spells.set( index, is_teachable( t_spell( index ) ) );
	}

	return true;
}

// -------------------------------------------------------------------
// -------------------------------------------------------------------
bool t_adventure_map::read_carryover_artifacts_from_map(
	std::streambuf &		stream,
	t_map_header const &	header )
{
	if ( header.version >= 22 )
	{
		if (	!m_carry_in_artifacts.read( stream )
			||	!m_carry_out_artifacts.read( stream ) )
			return false;
	}
	else
	{
		m_carry_in_artifacts.reset();
		m_carry_out_artifacts.reset();
	}

	return true;
}

// -------------------------------------------------------------------
// -------------------------------------------------------------------
bool t_adventure_map::read_cut_scene_info_from_map(
	std::streambuf &		stream,
	t_map_header const &	header )
{
	if ( header.version >= 24 )
	{
		m_prologue_info = header.prologue_info;
		m_epilogue_info = header.epilogue_info;
	}
	else
	{
		if ( header.version >= 21 )
		{
			if ( get< t_uint8 >( stream ) != 0 && !m_prologue_info.read_from_map( stream ) )
				return false;
			if ( get< t_uint8 >( stream ) != 0 && !m_epilogue_info.read_from_map( stream ) )
				return false;
		}
		else
		{
			m_prologue_info = t_scenario_cut_scene_info();
			m_epilogue_info = t_scenario_cut_scene_info();
		}
	}
	return true;
}

// -------------------------------------------------------------------
// adventure map class (for reading from a NEW game desrcription file)
// -------------------------------------------------------------------
bool t_adventure_map::read_from_map( std::streambuf&				buffer,
									 t_campaign_file_ref const &	file_ref,
									 int							map_num,
									 bool							multi_scenario,
									 t_progress_handler*			handler, 
									 t_player_setup const*			player_setup,
                                     t_difficulty					player_difficulty,
                                     int							turn_duration_index,
									 t_carryover_data_ptr			carry_in_data,
									 bool							guards_move )
{
	t_map_header header;

	// must reset the global counter
	m_global_id_factory.set_id_counter( k_first_valid_adventure_global_id );
	m_guards_can_move = guards_move;

	// Read the header
	if (handler != 0)
		handler->increment_maximum( k_object_progress_value );
	if (!::read( buffer, header, handler))
		return false;
	
	// Initialize ourselves appropriatly
	initialize( header.size, header.levels, header.random_seed, player_difficulty, turn_duration_index );

	m_carry_in_data = carry_in_data;
	if ( m_carry_in_data != 0 )
		m_carry_out_data = new t_carryover_data( *m_carry_in_data );
	else
		m_carry_out_data = 0;

	// read in subordinate data
	if (	!set_players( header, player_setup )
		||	!read_timed_events_from_map( buffer, header )
		||	!read_triggerable_events_from_map( buffer, header )
		||	!read_continuous_events_from_map( buffer, header )
		||	!read_placed_events_from_map( buffer, header ) 
		||  !read_obelisk_data_from_map( buffer, header ) 
		||  !read_player_data_body_from_map( buffer, header ) 
		||  !read_allowed_artifacts_from_map( buffer, header )
		||  !read_allowed_heroes_from_map( buffer, header )
		||  !read_allowed_skills_from_map( buffer, header )
		||  !read_allowed_spells_from_map( buffer, header )
		||	!read_cut_scene_info_from_map( buffer, header )
		||	!read_carryover_artifacts_from_map( buffer, header )
		||  !read_ai_importance_maps_from_map( buffer, header ) )
		return false;

	std::list<t_object_position> objects;

	if (!read_objects( buffer, objects, handler, header.version ))
		return false;

	t_level_map_point_2d       point;
	int               size;
	t_adventure_tile* tile;

	m_file_ref = file_ref;
	m_map_number = map_num;
	m_multi_scenario = multi_scenario;
	m_map_random_seed = header.random_seed;
	m_day = 0;
	
	set_map_name( header.name );
	set_map_difficulty( header.difficulty );
	set_map_description( header.description );

	for (point.level = 0; point.level < header.levels; point.level++)
	{
		for (point.row = 0; point.row < header.size; point.row++)
		{
			point.column = m_map.get_row_start( point.row );
			tile = &m_map.get( point );
			size = m_map.get_row_size( point.row );

			while (size--)
			{
				read_tile( buffer, *tile, point, header, handler );
				tile++;
			}
		}
	}
	if (header.version >= 2)
		read_heights( buffer, handler );

	// Make sure lava rivers are set as impassable objects
	mark_lava_rivers( *this );

	std::list< t_object_position >::iterator objects_end = objects.end();
	std::list< t_object_position >::iterator index;

	// Do preplacement initialization for all of the objects
	{
		std::list< t_adventure_object * > object_ptrs;

		// Call preplacement on all of the objects now and build a list of all
		// of the objects which need a second pass
		for ( index = objects.begin(); index != objects_end; ++index )
		{
			if ( index->object->preplacement( *this, 0 ) )
				object_ptrs.push_back( index->object.get() );
		}

		// Do subsequent passes over all of the objects until they are
		// completely initialized
		int pass = 1;
		while ( !object_ptrs.empty() )
		{
			std::list< t_adventure_object * >::iterator object_ptr_end = object_ptrs.end();
			std::list< t_adventure_object * >::iterator object_ptr_iter = object_ptrs.begin();
			while ( object_ptr_iter != object_ptr_end )
			{
				t_adventure_object & object = **object_ptr_iter;
				if ( !object.preplacement( *this, pass ) )
				{
					// This object is done, so remove it from the list
					object_ptr_iter = object_ptrs.erase( object_ptr_iter );
				}
				else
					++object_ptr_iter;
			}
			++pass;
		}
	}

	for (index = objects.begin(); index != objects_end; index++)
	{
		// Normally, map editor does not place objects on bridges
		index->object->place( *this, t_adv_map_point( index->point, false ) );
	}

	// cycle through objects which were actually placed, initializing them.
	int id;

	for (id = get_first_object_id(); id != k_invalid_object_id; id = get_next_object_id( id )) 
	{
		get_adv_object( id ).initialize( *this );
	}

	for (t_player_color color = t_player_color(0); color < k_player_gray; enum_incr( color ))
	{
		m_loss_condition_string[ color ] = header.loss_condition;
		m_victory_condition_string[ color ] = header.victory_condition;
	}

	// If a human-only team owns all present towns, disable the standard victory condition.
	m_standard_victory_condition = header.enable_standard_victory;

	if (m_standard_victory_condition)
	{
		if (which_team_owns_all_towns() == -1)
		{
			bool owned_towns_present = false;
			for (int i = 0; i < m_players.size(); i++)
			{
				if (header.playable[i] && (m_players[i]->get_town_count() > 0) )
				{
					owned_towns_present = true;
					break;
				}
			}
			m_standard_victory_condition = owned_towns_present;
		}
		else
			m_standard_victory_condition = false;
	}

	// Mark those players that start eliminated as eliminated.
	for (int i = 0; i < m_players.size(); i++)
	{
		if (m_players[i]->is_computer() && m_players[i]->has_lost())
			m_players[i]->eliminate();
	}

	// Set up first "month of"
	choose_next_month_creature();

	//enable event recording now.
	start_event_record();

	// Set up subterranean gates
	t_adv_subterranean_gate::calculate_pairs( this );

	if ( m_carry_out_data.get() != 0 )
		distribute_artifacts( *m_carry_out_data, *this );

	///////////////////////////////////////////////////////
	//replay/multiplay functionality.

	record_replay_shroud();
	
	///////////////////////////////////////////////////////

	return true;
}

void t_adventure_map::clear_saved_combat()
{
	m_saved_combat.reset();
}

// --------------------------------------------------------
// adventure map
// save game
// --------------------------------------------------------
void t_adventure_map::write( std::streambuf& buffer, t_progress_handler* handler,
							 t_saved_combat* saved_combat )
{
	// write out header
	t_saved_game_header header;
	int                 map_size;

	header.levels			= m_map.get_levels();
	header.map_file_ref		= m_file_ref;
	header.map_number		= m_map_number;
	header.multi_scenario	= m_multi_scenario;
	header.map_random_seed	= m_map_random_seed;
	header.random_seed		= random.get_seed();
	header.size				= m_map.get_size();

	header.map_name			= get_map_name();
	header.map_difficulty	= get_map_difficulty();
	header.player_difficulty	= get_player_difficulty();
	header.map_description	= get_map_description();
	header.guards_can_move	= guards_can_move();

	map_size = header.levels * (header.size * header.size + 4 * header.size - 3 ) / 2;
	handler->set_maximum( map_size * 10 + m_objects.size() * 10 );
	::write( buffer, header );


	// write out variables
	put<t_int8>( buffer, m_current_player_number );
	put<t_int8>( buffer, m_player_number );
	put<t_uint8>( buffer, m_in_neutral_turn );
	put<t_uint32>( buffer, m_single_use_object_count );
	put<t_uint16>( buffer, m_day );

	// write out terrain
	write_terrain_map( buffer );

	t_adv_map_point		 point;
	int					 size;
	int                  map_levels = m_map.get_levels();

	map_size   = m_map.get_size();

	// Write all of the cell vertices
	t_adventure_tile_vertex const* vertex;

	for ( point.level = 0; point.level < map_levels; ++point.level )
	{
		for ( point.row = 0; point.row < map_size + 1; ++point.row )
		{
			point.column = m_vertex_map.get_row_start( point.row );
			vertex = &m_vertex_map.get( point );
			size = m_vertex_map.get_row_size( point.row );
			handler->increment( 5 * size );
			while (size--)
			{
				put<t_uint8>( buffer, vertex->get_height() );
				vertex++;
			}
		}
	}

	// write list of used artifacts
	m_used_artifacts.write( buffer );

	// write out class which manages unused hero biographies
	m_default_heroes.write( buffer );

	std::vector<t_object_node>::const_iterator node;

	// write players
	int i;
	put<t_uint8>( buffer, m_players.size());
	for (i = 0; i < m_players.size(); i++)
		m_players[i]->write( buffer );

	if ( !write_shroud_transition_maps( buffer ) )
	{
		assert( false );
		return;
	}

	put<t_uint8>( buffer, 0 );  // space for the scripting system version number 

	// Write global scripts
	if ( !write_events( buffer ) ) {
		assert(false); 
		return;
	}

	if ( !write_script_variable_maps( buffer ) ) 
	{
		assert(false);
		return;
	}

	if ( !write_deletion_marker_map( buffer ) ) 
	{
		assert(false);
		return;
	}

	if ( !write_obelisk_data( buffer ) )
	{
		assert( false );
		return;
	}

	if ( !write_allowed_artifacts( buffer ) )
	{
		assert( false );
		return;
	}

	if ( !write_allowed_heroes( buffer ) )
	{
		assert( false );
		return;
	}
	
	if ( !write_allowed_skills( buffer ) )
	{
		assert( false );
		return;
	}
	
	if ( !write_allowed_spells( buffer ) )
	{
		assert( false );
		return;
	}

	if ( !write_winloss_strings( buffer ) )
	{
		assert( false );
		return;
	}

	if ( !write_monthly_creatures( buffer ) )
	{
		assert( false );
		return;
	}

	if ( !m_adventure_ai_ptr->write( buffer ) )
	{
		assert( false );
		return;
	}

	if ( !write_ai_importance_maps( buffer ) )
	{
		assert( false );
		return;
	}

	if ( !write_cut_scene_info( buffer ) )
	{
		assert( false );
		return;
	}

	if ( !write_carryover_artifacts( buffer ) )
	{
		assert( false );
		return;
	}

	if ( !write_carryover_data( buffer ) )
	{
		assert( false );
		return;
	}

	if ( !write_defeated_heroes( buffer ) )
	{
		assert( false );
		return;
	}

	// write objects
	t_adventure_object* object;

	size = 0;
	for (node = m_objects.begin(); node != m_objects.end(); node++)
		if (node->object != 0)
			size++;

	handler->increment( 10 * (m_objects.size() - size));

	put<t_uint32>( buffer, size );
	for (node = m_objects.begin(); node != m_objects.end(); node++)
	{
		if (node->object != 0)
		{
			handler->increment( 10 );
			object = node->object;

			if ( write_adventure_object( buffer, header , object ) == false )
				return ;
		}
	}

	// Write caravans last -- they need all map objects set up to initialize properly
	if ( !write_caravans( buffer ) )
	{
		assert( false );
		return;
	}

	//write out the event state cache, see function decription for its use.
	if ( !write_replay_event_state_cache( buffer ) )
	{
		assert( false );
		return;
	}

	//write out replay events for this turn block
	if ( !write_replay_events( buffer ) )
	{    
		assert( false );
		return;
	}

	// Write out the current value of the global ID counter
	m_global_id_factory.write( buffer );

	m_replay_terrain_map.write( buffer );

	m_replay_shroud_transition_map.write( buffer );
	
	put<unsigned int>( buffer , m_event_tick ); 
	if (saved_combat == 0)
		put<t_int8>( buffer, false );
	else
	{
		put<t_int8>( buffer, true );
		if (!saved_combat->write( buffer ))
			return;
	}

	put<bool>( buffer, m_standard_victory_condition );
	put<int>( buffer, m_victory_timer );
	put<int>( buffer, m_winning_team );

	put_bitset< k_obelisk_color_count >( buffer, m_obelisk_placed_treasure_flags );
	put_bitset< k_obelisk_color_count >( buffer, m_completed_obelisk_flags );
    for (t_obelisk_color color = k_obelisk_gold; color < k_obelisk_color_count; enum_incr( color ))
    {
        put<t_int32>(buffer, m_obelisk_artifact_position[ color ].row);
        put<t_int32>(buffer, m_obelisk_artifact_position[ color ].column);
        put<t_int8>(buffer, m_obelisk_artifact_position[ color ].level);
    }

}

// --------------------------------------------------------
// --------------------------------------------------------
bool t_adventure_map::write_defeated_heroes( std::streambuf& stream ) const
{
	put< t_uint16 >( stream, m_defeated_heroes.size() );
	t_hero_list::const_iterator 
		iter = m_defeated_heroes.begin(),
		iter_end = m_defeated_heroes.end();
	while (iter != iter_end)
	{
		if (!(*iter++)->write( stream ))
			return false;
	}
	return true;
}

// --------------------------------------------------------
// --------------------------------------------------------
bool t_adventure_map::read_defeated_heroes( std::streambuf & stream, int version )
{
	m_defeated_heroes.clear();

	if (version < 39)
		return true;

	int size = get< t_uint16 >( stream );
	m_defeated_heroes.resize( size );
	for (int i = 0; i < size; i ++)
	{
		t_counted_ptr< t_hero > hero_ptr = new t_hero;
		if ( !hero_ptr->read( stream ) )
			return false;
		m_defeated_heroes[i] = hero_ptr;
		add_mapped_name( hero_ptr->get_name(), hero_ptr );
	}
	return true;
}

// --------------------------------------------------------
// write_adventure_object : writes out the adventure object and its header informaitom. 
// it's the anaglous to the read_and_place_adventure_object. Both functions should be kept compatablie
// if a change is made in on or the other.
// --------------------------------------------------------
bool t_adventure_map::write_adventure_object ( std::streambuf& buffer, t_saved_game_header const & header, 
												t_adventure_object*  object ) const 
{	
	t_adv_map_point		 point;
	
	point = object->get_position();
	put<t_uint32>(buffer, point.row );
	put<t_uint32>(buffer, point.column);
	put<t_uint32>(buffer, point.level );
	put<bool>( buffer, point.on_bridge );
	put<t_uint16>(buffer, object->get_version() );
	write_adventure_global_id(buffer, object->get_global_id());
	object->write_object( buffer );

	return true;
}

// --------------------------------------------------------
// adventure map
// writes replay events for this turn block
// --------------------------------------------------------
bool		 t_adventure_map::write_replay_events( std::streambuf& stream ) const
{
	int event_size = m_event_buffer.size();

	put<t_uint32>(stream, event_size);
	
	t_adventure_event_list::const_iterator event_iter;
	
	for (event_iter = m_event_buffer.begin(); event_iter != m_event_buffer.end(); event_iter++)
	{
		put<t_uint32>(stream, event_iter->get()->get_event_type());

		if (event_iter->get()->write( stream ) == false)
			return false;
	}
	
	return true;
}

// --------------------------------------------------------
// adventure map
// writes the replay state cache for adv_objects, needed to properly record
// composition change to adv_objects
// --------------------------------------------------------
bool		 t_adventure_map::write_replay_event_state_cache( std::streambuf& stream ) const
{

	m_replay_event_state_map->write(stream);

	return true;
}

// --------------------------------------------------------
// adventure map
// load game from save file
// --------------------------------------------------------
bool t_adventure_map::read_and_place_adventure_object( std::streambuf& buffer, t_saved_game_header const & header, 
												t_adventure_object_ptr & object)
{
	//common function used by the save game load as well as the replay event scheme
	
	t_adv_map_point				point;
	int							version;
	bool						is_mobile;
	std::string					model_name;
	t_adventure_global_id		global_id;
	
	point.row		= get<t_uint32>(buffer);
	point.column	= get<t_uint32>(buffer);
	point.level		= get<t_uint32>(buffer);

	if (header.version >= 5)
		point.on_bridge = get<bool>( buffer );
	else
		point.on_bridge = false;

	version			= get<t_uint16>(buffer);
	
	if (header.version >= 21)
		global_id = read_adventure_global_id(buffer);
	
	is_mobile		= get<bool>( buffer );
	
	if (is_mobile)
	{
		////////////////////////////////////////////////
		//army object
		////////////////////////////////////////////////

		t_army_ptr army = new t_army;
		
		if (army->get_version() < version)
			return false;

		if (!army->read( buffer, version ))
			return false;
	
		if (header.version >= 21)
			army->set_global_id(global_id);

		army->place( *this, point );
		
		//cache_adventure_object_state( army );
	}
	else
	{
		////////////////////////////////////////////////
		//t_stationary_adventure_object object
		////////////////////////////////////////////////

		t_counted_ptr<t_stationary_adventure_object> file_object;
		t_qualified_adv_object_type					 type;
		
		read_string16( buffer, model_name );

		if (header.version < 11)
		{   // versions 10 and less did not have type information in the object
			file_object = create_adv_object( model_name );
			object = file_object.get();
			type = file_object->get_type();
		}
		else
		{
			// read type of object and compare it to the resource type
			t_qualified_adv_object_type		model_type;
			
			if (!::read( buffer, type ))
				return false;

			// create an object which matches the type in the file
			file_object = create_adv_object( model_name, type );
			model_type = file_object->get_type();

			// if the types match, we place that object
			if (type == model_type)
				object = file_object.get();
			else
				// if they don't, we create a new, blank object which
				// matches the type of the model and place that.
				object = create_adv_object( model_name, model_type );
		}
		
		if (file_object->get_version() < version)
			return false;

		if (!file_object->read( buffer, type, version ))
			return false;
				
		if (header.version >= 21)
			object->set_global_id(global_id);

		object->place( *this, point );
	}

	return true;
}

// --------------------------------------------------------
// adventure map
// load game from save file
// --------------------------------------------------------
bool t_adventure_map::read( std::streambuf& buffer, t_progress_handler* handler )
{
	// read header
	t_saved_game_header header;
	int					map_size;

	if (!::read( buffer, header, 0 ))
		return false;

	m_guards_can_move = header.guards_can_move;
	map_size = header.levels * (header.size * header.size + 4 * header.size - 3 ) / 2;
	handler->set_maximum( map_size * 10 + 50000 );
	m_file_ref = header.map_file_ref;
	m_map_number = header.map_number;
	m_multi_scenario = header.multi_scenario;
	m_map_random_seed = header.map_random_seed;
	random = t_random_number_generator( header.random_seed );

	// Initialize ourselves appropriatly
	initialize( header.size, header.levels, header.map_random_seed, header.player_difficulty, header.turn_duration_index );

	set_map_name( header.map_name );
	set_map_difficulty( header.map_difficulty );
	m_player_difficulty = header.player_difficulty;
	set_map_description( header.map_description );

	// read in variables
	m_current_player_number		= get<t_int8>( buffer );
	m_player_number				= get<t_int8>( buffer );
	if (header.version < 37)
		m_in_neutral_turn = false;
	else
		m_in_neutral_turn = (get<t_int8>( buffer ) != 0);
	m_single_use_object_count	= get<t_uint32>( buffer );
	m_day						= get<t_uint16>( buffer );

	// read terrain
	read_terrain_map( buffer , header.version );

	int					 size;

	read_heights( buffer, handler );

	// Make sure lava rivers are set as impassable objects
	mark_lava_rivers( *this );

	// read list of used artifacts
	if ( header.version < 13 )
	{
		m_used_artifacts = get_bitset<k_artifact_type_count>( buffer );
	}
	else
	{
		if ( !m_used_artifacts.read( buffer, function_2_handler( handle_used_artifacts ) ) )
			return false;
	}

	if (!m_default_heroes.read( buffer ))
		return false;

	// read players
	int i;

	size = get<t_uint8>( buffer);
	for (i = 0; i < size; i++)
	{
		t_shared_ptr<t_player> player( new t_player );

		player->read( buffer, header.version );
		m_players.push_back( player );
	}

	if (!read_shroud_transition_maps( buffer, header.version ))
		return false;

	if (header.version > 3)
		get<t_uint8>( buffer );  // scripting system version number 

	if (!read_events( buffer, header.version )) 
		return false;

	if (!read_script_variable_maps( buffer, header.version ))
		return false;

	if (!read_deletion_marker_map( buffer, header.version ))
		return false;

	if (!read_obelisk_data( buffer, header.version ))
		return false;

	if ( !read_allowed_artifacts( buffer, header.version ) )
		return false;

	if ( !read_allowed_heroes( buffer, header.version ) )
		return false;
	
	if ( !read_allowed_skills( buffer, header.version ) )
		return false;
	
	if ( !read_allowed_spells( buffer, header.version ) )
		return false;

	if ( !read_winloss_strings( buffer, header.version ) )
		return false;

	if ( !read_monthly_creatures( buffer, header.version ) )
		return false;

	if ( !m_adventure_ai_ptr->read( buffer, header.version ) )
		return false;

	if ( !read_ai_importance_maps( buffer, header.version ) )
		return false;

	if ( !read_cut_scene_info( buffer, header.version ) )
		return false;

	if ( !read_carryover_artifacts( buffer, header.version ) )
		return false;

	if ( !read_carryover_data( buffer, header.version ) )
		return false;

	if ( !read_defeated_heroes( buffer, header.version ) )
		return false;

	// read objects
	int						increment;
	std::string				model_name;

	size = get<t_uint32>( buffer );

	if (size > 0)
		increment = 50000 / size;

	while (size--)
	{
		handler->increment( increment );

		t_adventure_object_ptr object;

		//note object could be null even when return true, see function for reason.
		if (read_and_place_adventure_object( buffer , header , object ) == false)
			return false;
	}

	for ( int object_id = get_first_object_id(); object_id != k_invalid_object_id; object_id = get_next_object_id( object_id ) ) 
	{
		get_adv_object( object_id ).read_postplacement(*this);
	}

	// Read caravans last -- they need all map objects set up to initialize properly
	if ( !read_caravans( buffer, header.version ) )
		return false;
	
	if (header.version == 21 || header.version == 22)
	{
		if ( !read_replay_events( buffer , header.version ) )
			return false;
		
		if ( !read_replay_event_state_cache( buffer ) )
			return false;
	}
	
	if (header.version >= 23)
	{
		//the order of the event loading changed between (21-22) and 23+
		if ( !read_replay_event_state_cache( buffer ) )
			return false;

		if ( !read_replay_events( buffer , header.version ) )
			return false;
	}
	
	// must reset the unique id counter
	if (header.version >= 24) 
	{
		m_global_id_factory.read( buffer );
	} 
	else {
		// This is dangerous, but we'll do our best to be safe 
		// In a production environment we should never have a back rev save file so this
		// wouldn't happen.
		m_global_id_factory.set_id_counter( 0x80000000 );
	}
	
	if (header.version >= 26)
	{
		m_replay_terrain_map.read( buffer );
		
		m_replay_shroud_transition_map.read( buffer );
	}
	
	if ( header.version >= 27)
	{
		m_event_tick = get<unsigned int>( buffer);
	}
	else
	{
		m_event_tick = 0;
	}

	if ( header.version < 25 )
	{
		// Old save file with gate pairs not calculated
		t_adv_subterranean_gate::calculate_pairs( this );
	}
	

	if (header.version < 34)
	{
		m_saved_combat.reset();
	}
	else if (get<t_uint8>( buffer ) != 0)
	{
		m_saved_combat.reset( new t_saved_combat );
		if (!m_saved_combat->read( buffer, this ))
			return false;
	}

	if ( header.version < 35 ) 
	{
		m_standard_victory_condition = (which_team_owns_all_towns() != -1);
		m_victory_timer = -1;
		m_winning_team = -1;
	}
	else
	{
		m_standard_victory_condition = get< bool >( buffer );
		m_victory_timer = get< int >( buffer );
		m_winning_team = get< int >( buffer );
	}

	if ( header.version >= 41 ) 
	{
	    m_obelisk_placed_treasure_flags = get_bitset< k_obelisk_color_count >( buffer );
    }
	if ( header.version >= 43 ) 
	{
	    m_completed_obelisk_flags = get_bitset< k_obelisk_color_count >( buffer );
    }

	// Read the scores stored from previous maps in this multi-map campaign
	if ( header.version == 44 && is_multi_scenario() )
	{
		int map_number = get_map_number();
		int i;
		for ( i = 0; i < map_number; ++i )
		{
			int score = get< t_int32 >( buffer );
			if ( m_carry_in_data != 0 )
			{
				assert( m_carry_out_data != 0 );
				assert( m_carry_in_data->get_scenario_score_count() == i );
				assert( m_carry_out_data->get_scenario_score_count() == i );
				m_carry_in_data->add_scenario_score( score, 0 );
				m_carry_out_data->add_scenario_score( score, 0 );
			}
		}
	}
	
    if ( header.version >= 47 ) 
	{
        for (t_obelisk_color color = k_obelisk_gold; color < k_obelisk_color_count; enum_incr( color ))
        {
            m_obelisk_artifact_position[ color ].row        = get<t_int32>(buffer);
            m_obelisk_artifact_position[ color ].column     = get<t_int32>(buffer);
            m_obelisk_artifact_position[ color ].level      = get<t_int8>(buffer);
        }
    }

	start_event_record();

	return true;
}

// -------------------------------------------------------------------
// adventure map class. 
// read in the list of events for the saved replay turn block
// -------------------------------------------------------------------
bool		 t_adventure_map::read_replay_events( std::streambuf& stream , int version)
{
	int event_size = get<t_uint32>(stream);
	
	t_adventure_event_ptr		event;

	for (int i = 0; i < event_size; i++)
	{
		int event_type = get<t_uint32>(stream);
		
		switch(event_type)
		{
		case k_mover_event:
			event = new t_adventure_event_mover;
			break;
		case k_place_event:
			event = new t_adventure_event_place;
			break;
		case k_remove_event:
			event = new t_adventure_event_remove;
			break;
		case k_teleport_event:
			event = new t_adventure_event_teleport;
			break;
		case k_update_event:
			event = new t_adventure_event_update;
			break;
		case k_visiblity_event:
			event = new t_adventure_event_visiblity;
			break;
		case k_set_action_event:
			event = new t_adventure_event_set_action;
			break;
		case k_set_owner_event:
			event = new t_adventure_event_set_owner;
			break;
		case k_create_adv_spell_effects:
			event = new t_adventure_event_spells_effect;
			break;
		case k_look_trigger:
			event = new t_adventure_event_look_trigger;
			break;
		default:
			event = NULL;
			break;
		}
		
		if (event.get() == 0) return false;

		if (event->read( stream , version ) == false) 
			return false;
		
		event->set_handler( bound_handler( *this, &t_adventure_map::adventure_event_finished ) );
		event->attach_event_to_cache( this );

		m_event_buffer.push_back(event);
	}

	return true;
}

// -------------------------------------------------------------------
// adventure map class. 
// read in the cached state data for armies, so we can do state transitions correctly.
// -------------------------------------------------------------------
bool		 t_adventure_map::read_replay_event_state_cache( std::streambuf& stream )
{
	m_replay_event_state_map->read( stream );
	m_replay_event_state_map->attach_adv_objects_to_their_cache( this );

	return true;
}

// -------------------------------------------------------------------
// assign_global_id : the global ids have to be striclty managed to keep the m_global_id_map consistent.
// -------------------------------------------------------------------
void				 t_adventure_map::assign_global_id( t_adventure_object * object, t_adventure_global_id const & new_global_id )
{
	if (new_global_id == k_invalid_global_id)
		return;

	if (object != NULL)
	{
		object->set_global_id(new_global_id);

		//might want to include all objects into the map, however if this is going to be used by
		//only the event system, only recordable objects should be in the map to keep the memory
		//cost down.
		if (object->is_event_recordable())
			m_global_id_map[new_global_id] = object;
	}
	else
	{
		m_global_id_map.erase(new_global_id);
	}
}

// -------------------------------------------------------------------
// get_adventure_object_with_gid : using the global id retrive the adventure object from the map if it is in the map.
// -------------------------------------------------------------------
t_adventure_object * t_adventure_map::get_adventure_object_with_gid ( t_adventure_global_id const & global_id )
{
	if (global_id == k_invalid_global_id)
		return NULL;

	if (m_global_id_map.find(global_id) == m_global_id_map.end()) 
		return NULL;

	return m_global_id_map[global_id];
}

// -------------------------------------------------------------------
// adventure map class. 
// comput height based on object heights
// -------------------------------------------------------------------
int	t_adventure_map::compute_height( t_abstract_adv_object const& object,
									 t_level_map_point_2d const& point ) const
{
	if (!object.uses_bridge_heights() || !m_map.get( point ).has_bridge())
		return t_abstract_adventure_map::compute_height( object, point );

	int						height_sum = 0;
	int						num_vertices = 0;
	t_level_map_point_2d	vertex_point;
	t_footprint const&		footprint = object.get_footprint();
	t_map_point_2d			footprint_size = footprint.get_size();
	int						end_column;
	int						end_row;

	end_column = point.column + footprint_size.column;
	end_row = point.row + footprint_size.row;
	vertex_point.level = point.level;
	for (	vertex_point.column = point.column; vertex_point.column <= end_column;
			++vertex_point.column )
	{
		for (	vertex_point.row = point.row; vertex_point.row <= end_row;
				++vertex_point.row )
		{
			if ( is_vertex_valid( vertex_point ) )
			{
				++num_vertices;
				height_sum += m_vertex_map.get( vertex_point ).get_bridge_height();
			}
		}
	}

	if ( num_vertices == 0 )
		return 0;

	return ( height_sum + num_vertices / 2 ) / num_vertices;
}


// -------------------------------------------------------------------
// adventure map class. 
// mark heights based on object information
// -------------------------------------------------------------------
void t_adventure_map::stamp_object_height( t_adventure_object const& object )
{
	t_footprint const&  footprint = object.get_footprint();
	int					object_height;
	bool				found = false;
	t_map_point_2d      size = footprint.get_size();
	t_map_point_2d		offset;

	// determine if object has height information
	for (offset.row = 0; offset.row <= size.row && !found; offset.row++)
	{
		for (offset.column = 0; offset.column <= size.column; offset.column++)
		{
			if (footprint.get_vertex_height( offset ) != 0)
			{
				found = true;
				break;
			}
		}
	}
	if (!found)
		return;

	t_level_map_point_2d		origin = object.get_position();
	t_level_map_point_2d		point;
	t_adventure_tile_vertex*	vertex;
	int							height;

	object_height = t_adventure_map::compute_height( object, origin );
	for (offset.row = 0; offset.row <= size.row; offset.row++)
	{
		for (offset.column = 0; offset.column <= size.column; offset.column++)
		{
			height = footprint.get_vertex_height( offset );
			if (height == 0)
				continue;

			height += object_height;
			point = offset + origin;
			vertex = &m_vertex_map.get( point );
			if (vertex->get_bridge_height() < height)
				vertex->set_bridge_height( height );
		}
	}
}

// -------------------------------------------------------------------
// adventure map class.  Currently very basic.
// place object on adventure map and mark data structures
// -------------------------------------------------------------------
void t_adventure_map::stamp_object( t_object_id id )
{
	t_abstract_adventure_map::stamp_object( id );

	t_adventure_object const& object = *m_objects[id].object;
	t_footprint const&        footprint = object.get_footprint();
	t_map_point_2d            size = footprint.get_size();
	t_adv_map_point		      origin = object.get_position();
	t_adv_map_point			  point;
	t_map_point_2d            offset;
	t_adventure_tile*         tile;
	int						  edge;
	int						  on_bridge;
	bool					  is_bridge = object.is_bridge();
	bool					  is_ramp = object.is_ramp();

	// for each tile
	for (offset.row = 0; offset.row < size.row; offset.row++)
	{
		for (offset.column = 0; offset.column < size.column; offset.column++)
		{
			point = origin + offset;
			if (!m_map.is_valid( point ))
				continue;

			tile = &m_map.get( point );
			if (is_bridge)
				tile->change_bridge_count( 1 );
		
			if (is_ramp)
			{
				tile->change_ramp_count( 1 );

				// You can get off the bridge in a direction if 
				// that direction is not ground blocked and 
				// if the next square in that direction is not 
				// also part of the ramp
				if ( offset.row == 0 && !footprint.is_edge_blocked( offset, true, true )  )
				{
					tile->change_ramp_open_count( true, 1 );
				}

				if ( offset.column == 0 && !footprint.is_edge_blocked( offset, true, false )  )
				{
					tile->change_ramp_open_count( false, 1 );
				}
			}

			// mark tile and adjacent edges as blocked if tile is blocked
			if (footprint.is_cell_impassable( offset ))
			{
				mark_cell_as_impassable( *this, point, *tile );
			}

			// mark edges as blocked if objects edges are blocked
			// Note that for "historical reasons", is_edge_blocked
			// takes an under_bridge flag, while everything else 
			// takes an on_bridge flag
			for (on_bridge = 0; on_bridge < 2; ++on_bridge)
				for (edge = 0; edge < 2; ++edge)
					if (footprint.is_edge_blocked( offset, !on_bridge, edge == 1 ))
						tile->change_edge_block_count( edge == 1, on_bridge == 1, 1 );


			// add triggers
			if (footprint.is_trigger_cell( offset ))
				tile->add_trigger( id, point.on_bridge );
			if (footprint.is_left_edge_trigger( offset ))
				tile->increment_left_trigger( point.on_bridge );
			if (footprint.is_right_edge_trigger( offset ))
				tile->increment_right_trigger( point.on_bridge );
		}
	}
	// add triggers and blocking along leftmost edge
	offset.column = size.column;
	for (offset.row = 0; offset.row < size.row; offset.row++)
	{
		point = origin + offset;
		if (!m_map.is_valid( point ))
			continue;

		tile = &m_map.get( point );
		if (footprint.is_left_edge_trigger( offset ))
			tile->increment_left_trigger( point.on_bridge );
		for (on_bridge = 0; on_bridge < 2; ++on_bridge)
			if (footprint.is_left_edge_blocked( offset, !on_bridge ))
				tile->change_edge_block_count( false, on_bridge == 1, 1 );

		if ( is_ramp && !footprint.is_edge_blocked( offset, true, false )  )
		{
			tile->change_ramp_open_count( false, 1 );
		}
	}

	// add triggers and blocking along bottom row
	offset.row = size.row;
	for (offset.column = 0; offset.column < size.column; offset.column++)
	{
		point = origin + offset;
		if (!m_map.is_valid( point ))
			continue;

		tile = &m_map.get( point );
		if (footprint.is_right_edge_trigger( offset ))
			tile->increment_right_trigger( point.on_bridge );
		for (on_bridge = 0; on_bridge < 2; ++on_bridge)
			if (footprint.is_right_edge_blocked( offset, !on_bridge ))
				tile->change_edge_block_count( true, on_bridge == 1, 1 );

		if ( is_ramp && !footprint.is_edge_blocked( offset, true, true )  )
		{
			tile->change_ramp_open_count( true, 1 );
		}
	}
	// if this object has heights, mark them in the map
	stamp_object_height( object );
}

// -------------------------------------------------------------------
// adventure map class.  Currently very basic.
// -------------------------------------------------------------------
void t_adventure_map::unstamp_object( t_object_id id )
{
	t_abstract_adventure_map::unstamp_object( id );

	t_adventure_object const&	object = *m_objects[id].object;
	t_footprint const&			footprint = object.get_footprint();
	t_map_point_2d				size = footprint.get_size();
	t_adv_map_point				origin = object.get_position();
	t_adv_map_point				point;
	t_map_point_2d				offset;
	t_adventure_tile*			tile;
	t_adventure_tile*			next_tile;
	int							edge;
	int							on_bridge;
	t_level_map_point_2d		next_point;
	bool						is_bridge = object.is_bridge();
	bool						is_ramp = object.is_ramp();

	for (offset.row = 0; offset.row < size.row; offset.row++)
	{
		for (offset.column = 0; offset.column < size.column; offset.column++)
		{
			point = origin + offset;
			if (!m_map.is_valid( point ))
				continue;

			tile = &m_map.get( point );
			if (is_bridge)
				tile->change_bridge_count( -1 );
			
			if (is_ramp)
			{
				tile->change_ramp_count( -1 );
				if ( offset.row == 0 && !footprint.is_edge_blocked( offset, true, true )  )
				{
					tile->change_ramp_open_count( true, -1 );
				}

				if ( offset.column == 0 && !footprint.is_edge_blocked( offset, true, false )  )
				{
					tile->change_ramp_open_count( false, -1 );
				}
			}

			if (footprint.is_cell_impassable( offset ))
			{
				assert( !point.on_bridge ); // Tiles are not set up to handle permanently blocking objects on bridges
				for (edge = 0; edge < 2; ++edge)
				{
					tile->change_edge_block_count( edge == 1, point.on_bridge, -1 );
					next_point = point + k_edge_offset[edge];
					if (m_map.is_valid( next_point ))
					{
						next_tile = &m_map.get( next_point );
						next_tile->change_edge_block_count( edge == 1, point.on_bridge, -1 );
					}
				}
				tile->decrement_block();
			}
			if (footprint.is_trigger_cell( offset ))
				tile->remove_trigger( id, point.on_bridge );
			if (footprint.is_left_edge_trigger( offset ))
				tile->decrement_left_trigger( point.on_bridge );
			if (footprint.is_right_edge_trigger( offset ))
				tile->decrement_right_trigger( point.on_bridge );

			// Note that for "historical reasons", is_edge_blocked
			// takes an under_bridge flag, while everything else 
			// takes an on_bridge flag
			for (on_bridge = 0; on_bridge < 2; ++on_bridge)
				for (edge = 0; edge < 2; ++edge)
					if (footprint.is_edge_blocked( offset, !on_bridge, edge == 1 ))
						tile->change_edge_block_count( edge == 1, on_bridge == 1, -1 );
		}
	}
	offset.column = size.column;
	for (offset.row = 0; offset.row < size.row; offset.row++)
	{
		point = origin + offset;
		if (!m_map.is_valid( point ))
			continue;

		tile = &m_map.get( point );
		if (footprint.is_left_edge_trigger( offset ))
			tile->decrement_left_trigger( point.on_bridge );
		for (on_bridge = 0; on_bridge < 2; ++on_bridge)
			if (footprint.is_left_edge_blocked( offset, !on_bridge ))
				tile->change_edge_block_count( false, on_bridge == 1, -1 );

		if ( is_ramp && !footprint.is_edge_blocked( offset, true, false )  )
		{
			tile->change_ramp_open_count( false, 1 );
		}
	}
	offset.row = size.row;
	for (offset.column = 0; offset.column < size.column; offset.column++)
	{
		point = origin + offset;
		if (!m_map.is_valid( point ))
			continue;

		tile = &m_map.get( point );
		if (footprint.is_right_edge_trigger( offset ))
			tile->decrement_right_trigger( point.on_bridge );
		for (on_bridge = 0; on_bridge < 2; ++on_bridge)
			if (footprint.is_right_edge_blocked( offset, !on_bridge ))
				tile->change_edge_block_count( true, on_bridge == 1, -1 );
		if ( is_ramp && !footprint.is_edge_blocked( offset, true, true )  )
		{
			tile->change_ramp_open_count( true, 1 );
		}
	}
}

// --------------------------------------------------------
// adventure map
// determine if a trigger event occurs if moving in direction X from point
// --------------------------------------------------------
t_adventure_object* t_adventure_map::get_trigger_object( t_adv_map_point const&  point, 
														 t_direction			 direction,
														 t_creature_array const* army ) const
{
	t_map_point_2d      offset =  get_direction_offset( direction );
	t_adv_map_point     new_point( point + offset );
	t_adventure_object*	result = 0;

	if ( !is_valid( new_point ) )
		return 0;

	// special on/off ramp handling
	if ( is_ramp_open( point, direction ) )
		new_point.on_bridge = !point.on_bridge;

	result = get_trigger_object( new_point, army );
	if (result == 0)
		return 0;
	
	t_adventure_tile const& new_tile = m_map.get( new_point );

	// determine if we're crossing a legal trigger boundry
	if (offset.column > 0 && new_tile.left_is_trigger( new_point.on_bridge ))
		return result;

	if (offset.row > 0 && new_tile.right_is_trigger( new_point.on_bridge ))
		return result;

	// note: cannot just use point, because of diagonals.  So instead
	// we look at the tile to the left or right of the target to 
	// check the edges.
	if (offset.column < 0)
	{
		t_adv_map_point newer_point = point + t_map_point_2d( offset.row, 0 );
		newer_point.on_bridge = new_point.on_bridge;
		if (m_map.is_valid( newer_point ) && m_map.get( newer_point ).left_is_trigger( newer_point.on_bridge ))
			return result;
	}
	if (offset.row < 0)
	{
		t_adv_map_point newer_point = point + t_map_point_2d( 0, offset.column );
		newer_point.on_bridge = new_point.on_bridge;
		if (m_map.is_valid( newer_point ) && m_map.get( newer_point ).right_is_trigger( newer_point.on_bridge ))
			return result;
	}
	return 0;
}

// --------------------------------------------------------
// adventure map
// find trigger object in a cell.
// --------------------------------------------------------
t_adventure_object* t_adventure_map::get_trigger_object( t_adv_map_point  const& point, 
														 t_creature_array const* army ) const
{
	t_adventure_tile const& new_tile = m_map.get( point );

	if (!new_tile.is_trigger( point.on_bridge ))
		return 0;

	t_adventure_object*		result = 0;
	t_adventure_object*		object;

	const std::vector<int>& triggers = new_tile.get_triggers(point.on_bridge);
	for (std::vector<int>::const_iterator index = triggers.begin(); index != triggers.end(); ++index)
	{
		object = m_objects[*index].object;
		if (army != 0 && !object->is_triggered_by( *army ))
			continue;
		if (dynamic_cast<t_army*>(result) != 0 && dynamic_cast<t_army*>(object) == 0)
			continue;
		result = object;
	}
	return result;
}

// --------------------------------------------------------
// adventure map
// activate a trigger event occurs by moving in direction X from point
// --------------------------------------------------------
bool t_adventure_map::activate_trigger( t_army* army, t_adv_map_point const& point,
		                                t_direction direction, t_adventure_frame* frame )
{
	t_adventure_object_ptr object = get_trigger_object( point, direction, army );
	t_army_ptr counted_army = army;

	if (object == 0)
		return false;
	army->set_path( t_adventure_path() );
	frame->refresh_path();
	object->activate_trigger( army, point, direction, frame );
	post_trigger( army, object, frame );
	return true;
}

void t_adventure_map::post_trigger( t_army* army, t_adventure_object* object,
								    t_adventure_frame* frame )
{
	if (!is_game_over())
	{
		frame->evaluate_victory_condition();
		if (is_game_over())
			return;
		process_continuous_events( frame );
		if (is_game_over())
			return;
		frame->evaluate_victory_condition();
		if (is_game_over())
			return;

		army->update_state();
		object->update_state();
		if (army == frame->get_selected_army())
			set_music( get_tile( army->get_position() ).get_terrain() );
		frame->reset_pathfinder();
		frame->update();
	}
}

bool t_adventure_map::corners_block( t_adv_map_point const&	point,
									 t_adv_map_point const&	new_point,
								     t_direction	        direction ) const
{
	if (direction & 1)
	{
		return is_blocked( point, clockwise( direction ) )
			|| is_blocked( point, counter_clockwise( direction ) )
			|| is_blocked( new_point, clockwise( direction, 3 ) )
			|| is_blocked( new_point, counter_clockwise( direction, 3 ) );
	}
	return false;
}

// --------------------------------------------------------
// adventure map
// determine if it is possible to travel across an edge
// --------------------------------------------------------
bool t_adventure_map::is_blocked( t_adv_map_point const&	  point,
								  t_direction		          direction ) const
{
	t_adv_map_point new_point( point + get_direction_offset( direction ) );

	if (!is_valid( new_point ))
		return true;

	if (corners_block( point, new_point, direction ))
		return true;

	if ( is_ramp_open( point, direction ) )
	{
		return m_map.get( new_point ).is_blocked( !point.on_bridge );
	}
	if ((direction & 1) == 1)
		return false;

	if (direction == k_direction_northeast || direction == k_direction_northwest)
	{
		assert( is_valid( point ) );
		return m_map.get( point ).is_edge_blocked( direction == k_direction_northeast,
												   new_point.on_bridge );
	}
	return m_map.get( new_point ).is_edge_blocked( direction == k_direction_southwest,
												   new_point.on_bridge );
}


// --------------------------------------------------------
// adventure map
// Can we get on or off a bridge here?
// --------------------------------------------------------
bool t_adventure_map::is_ramp_open( t_adv_map_point const & point, t_direction direction ) const
{
	t_adv_map_point new_point( point + get_direction_offset( direction ) );

	if (!is_valid( new_point ))
		return false;

	assert( is_valid( point ) );

	t_adventure_tile const & tile = m_map.get( point );
	t_adventure_tile const & new_tile = m_map.get( new_point );

	if ( point.on_bridge && !tile.has_ramp() )
		return false;

	// Only get on bridge on ramps. Only get off bridges if there are no ramps
	if ( point.on_bridge == new_tile.has_ramp() )
		return false;

	if (direction & 1)
	{
		return is_ramp_open( point, clockwise( direction ) )
			|| is_ramp_open( point, counter_clockwise( direction ) )
			|| is_ramp_open( new_point, clockwise( direction, 3 ) )
			|| is_ramp_open( new_point, counter_clockwise( direction, 3 ) );
	}
	
	if (direction == k_direction_northeast || direction == k_direction_northwest)
	{
		return m_map.get( point ).is_ramp_open( direction == k_direction_northeast );
	}
	else
	{
		return m_map.get( new_point ).is_ramp_open( direction == k_direction_southwest );
	}
}


// --------------------------------------------------------
// adventure map
// clear displayed path
// --------------------------------------------------------
void t_adventure_map::clear_path()
{
	int i;

	for (i = 0; i < m_path_display.size(); i++)
		m_path_display[i]->destroy();
	m_path_display.clear();
}

// --------------------------------------------------------
// clear all tiles of danger
// --------------------------------------------------------
static void clear_danger( t_adventure_map& map )
{
	int					size = map.get_size();
	int					level_count = map.get_num_levels();
	t_adv_map_point		point;
	t_adventure_tile*	tile;

	for ( point.level = 0; point.level < level_count; ++point.level )
	{
		for ( point.row = 0; point.row < size; ++point.row )
		{
			int row_end = map.get_row_end( point.row );

			point.column = map.get_row_start( point.row );
			tile = &map.get_adv_tile( point );
			for ( ; point.column < row_end; ++point.column )
			{
				tile->set_dangerous( false, false );
				tile->set_dangerous( true, false );
				++tile;
			}
		}
	}
}

// --------------------------------------------------------
// mark attack areas
// --------------------------------------------------------
static void mark_attack_area( t_army& army, t_adventure_map& map, 
							  t_adventure_path_finder& path_finder )
{
	int						move_limit = army.get_max_movement();
	t_visited_adv_points	visited_points;

	path_finder.seed_position( visited_points, move_limit );
	
	t_visited_adv_points::iterator	index = visited_points.begin();
	t_visited_adv_points::iterator	end = visited_points.end();
	t_adv_map_point					point;
	t_adventure_tile*				tile;

	point = army.get_position();
	tile = &map.get_adv_tile( point );
	tile->set_dangerous( point.on_bridge, true );
	for (; index != end; ++index)
	{
		point = *index;
		tile = &map.get_adv_tile( point );
		tile->set_dangerous( point.on_bridge, true );
	}
}

// --------------------------------------------------------
// display path
// --------------------------------------------------------
void t_adventure_map::mark_danger()
{
	if (m_danger_marked)
		return;

	if (m_map_window == 0)
		return;

	clear_danger( *this );
	m_danger_marked = true;

	t_adventure_frame*	frame = m_map_window->get_frame();
	t_army*				army = frame->get_selected_army();

	if (army == 0)
		return;

	if (!army->is_human())
		return;

	t_player*	player = army->get_owner();
	int			team = player->get_team();


	t_army_list::iterator	index;
	t_army_list::iterator	end = m_armies.end();
	t_army*					enemy;
	t_adventure_path_finder path_finder( *this );

	path_finder.set_path_type( k_path_search_player_danger );
	for ( index = m_armies.begin(); index != end; ++index )
	{
		enemy = *index;
		if (same_team( player, enemy->get_owner() ))
			continue;
		if (!enemy->is_visible_to( team ))
			continue;
		if (enemy->empty() || enemy->is_graveyard())
			continue;

		path_finder.set_army( enemy );
		mark_attack_area( *enemy, *this, path_finder );
	}
}

// --------------------------------------------------------
// adventure map
// display a new path using arrows
// --------------------------------------------------------
void t_adventure_map::display_path( t_adventure_path const& path, int movement )
{
	if ( m_is_replay_map == true ) 
		return;

	int i;
	t_direction            direction;
	t_direction            turn;
	t_adventure_path_point point;
	t_adventure_object*    object;
	t_path_arrow_color		color;

	clear_path();
	i = path.size();
	if (i > 0)
		mark_danger();
	while (i--)
	{
		point = path[i];
		direction = point.direction;
		if (i == 0)
			turn = direction = k_direction_count;
		else
			turn = path[i-1].direction;

		if (point.move_cost > movement)
			color = k_path_arrow_red;
		else if (get_adv_tile( point ).is_dangerous( point.on_bridge ))
			color = k_path_arrow_yellow;
		else
			color = k_path_arrow_green;
		object = new t_path_arrow( direction, turn, color );
		object->place( *this, point );
		m_path_display.push_back( object );
	}
}

// --------------------------------------------------------
// adventure map
// clear selection icon
// --------------------------------------------------------
void t_adventure_map::clear_selection()
{
	if (m_selected_icon == 0)
		return;
	m_selected_icon->destroy();
	m_selected_icon = 0;
}

// --------------------------------------------------------
// adventure map
// display a selection icon
// --------------------------------------------------------
void t_adventure_map::display_selection( t_adventure_object* object )
{
	if (( m_is_replay_map == true ) || (object->get_map_id() == -1))
		return;

	t_adv_map_point point = object->get_position();
	bool			was_floating = object->is_floating();

	clear_selection();

	if (!was_floating)
		object->float_object();

	m_selected_icon = new t_selection_marker( 0 );
	m_selected_icon->place( *this, point );

	if (!was_floating)
		object->sink_object();
}

// --------------------------------------------------------
// adventure map
// --------------------------------------------------------
int t_adventure_map::get_first_object_id() const
{
	return m_first_object_id;
}

// --------------------------------------------------------
// adventure map
// --------------------------------------------------------
int t_adventure_map::get_last_object_id() const
{
	return m_last_object_id;
}

// --------------------------------------------------------
// adventure map
// --------------------------------------------------------
int t_adventure_map::get_next_object_id( t_object_id id ) const
{
	if (id == k_invalid_object_id)
		return id;
	assert( id < m_objects.size() && id >= 0 );
	return m_objects[id].next;
}

// --------------------------------------------------------
// adventure map
// --------------------------------------------------------
t_abstract_adv_object& t_adventure_map::get_object( t_object_id id )
{
	assert( id != k_invalid_object_id && id < m_objects.size() && id >= 0);
	return *m_objects[id].object;
}

// --------------------------------------------------------
// adventure map
// --------------------------------------------------------
t_abstract_adv_object const& t_adventure_map::get_object( t_object_id id ) const
{
	assert( id != k_invalid_object_id && id < m_objects.size() && id >= 0);
	return *m_objects[id].object;
}

// --------------------------------------------------------
// adventure map
// --------------------------------------------------------
t_level_map_point_2d t_adventure_map::get_object_pos( t_object_id id ) const
{
	assert( id != k_invalid_object_id && id < m_objects.size() && id >= 0);
	return m_objects[id].object->get_position();
}

// --------------------------------------------------------
// adventure map
// --------------------------------------------------------
int t_adventure_map::get_prev_object_id( t_object_id id ) const
{
	if (id == k_invalid_object_id)
		return id;
	assert( id < m_objects.size() && id >= 0 );
	return m_objects[id].next;
}

// --------------------------------------------------------
// adventure map
// --------------------------------------------------------
bool t_adventure_map::is_floating( t_object_id id ) const
{
	return m_objects[id].object->is_floating();
}

// --------------------------------------------------------
// adventure map
// --------------------------------------------------------
int t_adventure_map::get_current_player_number() const
{
	return m_current_player_number;
}

// --------------------------------------------------------
// adventure map
// --------------------------------------------------------
t_player& t_adventure_map::get_current_player()
{
	return *m_players[m_current_player_number];
}

// --------------------------------------------------------
// adventure map
// --------------------------------------------------------
t_player const& t_adventure_map::get_current_player() const
{
	return *m_players[m_current_player_number];
}

// ------------------------------------------------------------------
// check if team has any human players
// ------------------------------------------------------------------
bool t_adventure_map::has_human_players( int team ) const
{
	int i;

	for (i = 0; i < m_players.size(); i++)
		if (m_players[i]->get_team() == team && !m_players[i]->is_computer())
			return true;
	return false;
}

// ------------------------------------------------------------------
// make sure standard victory condition is properly set
// ------------------------------------------------------------------
int t_adventure_map::which_team_owns_all_towns() const
{
	int winning_team = -1;
	bool all_computer = true;

	for (int i = 0; i < m_players.size(); i++)
	{
		// check to see if same team owns all the towns
		if( m_players[i]->get_town_count() > 0 )
		{
			int current_team = m_players[i]->get_team();
			if ((winning_team != -1) && (winning_team != current_team))
				return -1;	// more than one team has towns!

			if (!m_players[i]->is_computer())
				all_computer = false;

			winning_team = current_team;
		}
	}

	// Teams made up exclusively of computer players are not valid.
	if (all_computer)
		winning_team = -1;

	return winning_team; // it's possible that no one owns any towns
}

// ------------------------------------------------------------------
// initialize all players
// ------------------------------------------------------------------
void t_adventure_map::initialize_players( t_difficulty difficulty )
{
	int i;

	for (i = 0; i < m_players.size(); i++)
		m_players[i]->initialize( !has_human_players( m_players[i]->get_team() ), difficulty );
	m_current_player_number = 0;
}

// ------------------------------------------------------------------
// set the current player
// ------------------------------------------------------------------
void t_adventure_map::set_current_player( int arg )
{
	m_current_player_number = arg;

	t_player const& player = *m_players[m_current_player_number];

	if (is_hotseat() && !player.is_computer())
	{
		m_player_number = m_current_player_number;
	}
}


// --------------------------------------------------------
// adventure map
// --------------------------------------------------------
void t_adventure_map::add_free_materials()
{
	t_player& player = get_player();

	player.gain( k_gold, 200000 );

	t_material material;

	for (material = k_wood; material < k_material_count; enum_incr( material ))
		player.gain( material, 300 );
}

// --------------------------------------------------------
// adjust AI income
// --------------------------------------------------------
static void adjust_ai_income( t_player& player, t_difficulty difficulty, int day )
{
	static float const k_multiplier[k_difficulty_count] =
	{ 0.75, 1.0, 1.25, 1.50, 2.0 };

	if (!player.is_computer())
		return;

	float				factor = k_multiplier[difficulty];
	t_material_array	income = player.get_income( day );
	t_material			material;

	for (material = t_material(0); material < k_material_count; enum_incr(material))
	{
		double	bonus = income[material] * (factor - 1.0);
		long	result = bonus;
		long	remainder = bonus * 7.0 - result * 7.0 + 0.5;

		// shift when the bonus arrives by the day, to spread bonus remainders through
		// the week.
		day = (day - material + 6) % 7;
		if (day < remainder)
			result++;
		if (-result > player.get_funds()[material])
			result = -player.get_funds()[material];
		player.gain( material, result );
	}
}

// --------------------------------------------------------
// adventure map
// --------------------------------------------------------
void t_adventure_map::process_new_day()
{
	m_day++;

	if ( ( m_day % k_days_per_month ) == 0 )
	{
		// New month
		do_new_month();
	}

	int id = m_first_object_id;

	while (id != k_invalid_object_id)
	{
		m_objects[id].object->preprocess_new_day();
		id = m_objects[id].next;
	}

	id = m_first_object_id;

	while (id != k_invalid_object_id)
	{
		m_objects[id].object->process_new_day();
		id = m_objects[id].next;
	}

	int i;
	std::vector<bool> has_human;

	has_human.resize( m_players.size() );
	for (i =0 ; i < has_human.size(); ++i)
		has_human[i] = false;
	for (i = 0; i < m_players.size(); ++i)
		if (!m_players[i]->is_computer())
			has_human[m_players[i]->get_team()] = true;

	for (i = 0; i < m_players.size(); ++i)
		if (has_human[ !m_players[i]->get_team() ])
			adjust_ai_income( *m_players[i], m_player_difficulty, m_day );

	// Allow creatures in caravans to process new day
	t_caravan_set::iterator caravan_index;

	for ( caravan_index = m_caravans.begin(); caravan_index != m_caravans.end(); caravan_index++ )
	{
		(*caravan_index)->process_new_day();
	}
}

// --------------------------------------------------------
// Do all the stuff needed when a new month starts
// --------------------------------------------------------
void t_adventure_map::do_new_month()
{
	static std::bitset< k_creature_type_count > const & k_respawn_as_water_creature = get_respawn_as_water_creature();
	static std::bitset< k_creature_type_count > const & k_respawn_as_land_creature = get_respawn_as_land_creature();

	// Percent of possible respawn points that respawn
	int const k_respawn_percent = 50;

	// Respawn some creatures in the same location as the original wandering monsters
	// Only respawn creatures of the same alignment as the original, and don't make
	// them more powerful than the original stack would have been if it was killed.
	// Respawn about 1/2 of those qualifying (but at least 1 if possible)
	t_creature_traits const & respawn_traits = get_traits( m_next_month_creature );
	bool respawned_one = false; // At least one should respawn if possible

	t_respawn_point_list::iterator respawn_iter = m_respawn_point_list.begin();

	while ( respawn_iter != m_respawn_point_list.end() )
	{
		bool respawn_this = true;

		if ( respawn_iter->get_on_water() )
		{
			if ( !k_respawn_as_water_creature[ m_next_month_creature ] )
			{
				respawn_this = false;
			}
		}
		else
		{
			if (    !k_respawn_as_land_creature[ m_next_month_creature ]
				 || respawn_iter->get_alignment() != respawn_traits.alignment ) 
			{
				respawn_this = false;
			}
		}

		if ( respawn_this && ( respawn_iter->get_current_xp( m_day ) < respawn_traits.experience ) )
		{
			respawn_this = false; // Not enough xp for even 1 creature
		}

		if ( respawn_this && respawned_one && ( random(1, 100) > k_respawn_percent ) )  
		{
			respawn_this = false; // Bad luck 
		}

		if ( respawn_this && respawn_iter->respawn( *this ) )
		{
			respawned_one = true;

			respawn_iter = m_respawn_point_list.erase( respawn_iter );
		}
		else
		{
			respawn_iter++;
		}
	}

	choose_next_month_creature();
}


// --------------------------------------------------------
// adventure map
// Choose the next "month of" event. This occurs 1 month
// early so that Potions of Precog work right & to cut down
// on save/reload cheats slightly
// --------------------------------------------------------
void t_adventure_map::choose_next_month_creature()
{
	static std::bitset< k_creature_type_count > const & k_respawn_as_water_creature = get_respawn_as_water_creature();
	static std::bitset< k_creature_type_count > const & k_respawn_as_land_creature = get_respawn_as_land_creature();

	// Can creatures of this type respawn anywhere?
	t_static_vector< int, k_creature_type_count > respawn_points_by_creature_type;
	std::fill( respawn_points_by_creature_type.begin(), respawn_points_by_creature_type.end(), 0 );

	// NOTE: treating "water" creatures as special pseudo alignment
	int const k_water_alignment = k_town_type_count;

	t_static_vector< int, k_town_type_count + 1 > xp_by_alignment;
	std::fill( xp_by_alignment.begin(), xp_by_alignment.end(), 0 );

	t_static_vector< int, k_town_type_count + 1 > respawn_points_by_alignment;
	std::fill( respawn_points_by_alignment.begin(), respawn_points_by_alignment.end(), 0 );

	std::bitset< k_town_type_count + 1> has_a_respawn_point_by_alignment;
	has_a_respawn_point_by_alignment.reset();

	// The list of respawn points to process
	t_respawn_point_list * point_list;

	// Storage for army based list, if needed
	t_respawn_point_list army_based_list;

	if ( m_respawn_point_list.empty() )
	{
		// Choose based on wandering monsters
		t_army_list::iterator army_iter;
		for ( army_iter = get_armies_begin(); army_iter != get_armies_end(); army_iter++ )
		{
			if ( (*army_iter)->get_owner_color() == k_player_gray )
			{
				army_based_list.push_back( (*army_iter)->get_respawn_data() );
			}
		}

		point_list = &army_based_list;
	}
	else
	{
		point_list = &m_respawn_point_list;
	}

	// Start of next month
	int next_month_date = ( ( m_day / k_days_per_month ) + 1 ) * k_days_per_month;

	t_respawn_point_list::iterator respawn_iter;

	for ( respawn_iter = point_list->begin(); respawn_iter != point_list->end(); respawn_iter++ )
	{
		int xp = respawn_iter->get_current_xp( next_month_date );
		if ( respawn_iter->get_on_water() )
		{
			xp_by_alignment[ k_water_alignment ] += xp;
			respawn_points_by_alignment[ k_water_alignment ]++;
		}
		else
		{
			xp_by_alignment[ respawn_iter->get_alignment() ] += xp;
			respawn_points_by_alignment[ respawn_iter->get_alignment() ]++;
		}

		for ( t_creature_type creature = t_creature_type(0); creature < k_creature_type_count; enum_incr( creature ) )
		{
			t_creature_traits const & traits = get_traits( creature );

			if ( traits.experience <= xp )
			{
				if ( respawn_iter->get_on_water() )
				{
					if ( k_respawn_as_water_creature[ creature ] )
					{
						respawn_points_by_creature_type[ creature ]++;
						has_a_respawn_point_by_alignment[ k_water_alignment] = true;
					}
				}
				else
				{
					if (   k_respawn_as_land_creature[ creature ] 
						&& traits.alignment == respawn_iter->get_alignment() )
					{
						respawn_points_by_creature_type[ creature ]++ ;
						has_a_respawn_point_by_alignment[ traits.alignment] = true;
					}
				}
			}
		}
	}

	// The list of possible creatures to respawn
	std::list< t_creature_type > creature_types;

	int total_xp = 0;
	int alignment;

	for ( alignment = 0; alignment < ELEMENTS_OF( xp_by_alignment ); alignment++ )
	{
		if ( has_a_respawn_point_by_alignment[ alignment ] )
			total_xp += xp_by_alignment[ alignment ];
	}

	// Choose an alignment
	if ( total_xp > 0 )
	{
		int chosen_xp = random( total_xp );

		for ( alignment = 0; alignment < xp_by_alignment.size(); alignment++ )
		{
			if ( has_a_respawn_point_by_alignment[ alignment ] )
			{
				chosen_xp -= xp_by_alignment[ alignment ];

				if ( chosen_xp <= 0 )
					break;
			}
		}
	
		assert( alignment < xp_by_alignment.size());

		if ( alignment >= xp_by_alignment.size() )
		{
			alignment = k_town_might;
		}

		// Find creatures with that alignment
		for ( int pass = 0; creature_types.empty() && pass < 2; pass++ )
		{
			for ( t_creature_type creature = t_creature_type(0); creature < k_creature_type_count; enum_incr( creature ) )
			{
				t_creature_traits const & creature_traits = get_traits( creature );
				if ( alignment == k_water_alignment )
				{
					if ( !k_respawn_as_water_creature[ creature ] )
						continue;
				}
				else
				{
					if ( !k_respawn_as_land_creature[ creature ] )
						continue;

					if ( creature_traits.alignment != alignment )
						continue;
				}

				if ( pass == 0 )
				{
					// Must have at least 1/4 of possible respawn points available 
					if ( respawn_points_by_creature_type[ creature ] * 4 < respawn_points_by_alignment[ alignment ] )
						continue;

					// Only spawn 1st & 2nd level creatures on first month. Even if the levels are
					// balanced, 3rd and 4th level creatures seem to difficult at the early stages
					// of a map
					if ( creature_traits.level > 2 && next_month_date <= k_days_per_month )
						continue;
				}
				else
				{
					// Second Pass: take anything with a respawn point
					if ( respawn_points_by_creature_type[ creature ] == 0 )
						continue;
				}

				creature_types.push_back( creature );
			}
		}
	}

	if ( creature_types.empty() )
	{
		// Fall back position for cases like no wandering monsters on map:
		// Randomly choose a first level creature
		for ( t_creature_type creature = t_creature_type(0); creature < k_creature_type_count; enum_incr( creature ) )
		{
			if ( get_traits( creature ).level == 1 )
			{
				creature_types.push_back( creature );
			}
		}
	}

	// And the winner is...
	m_previous_month_creature = m_next_month_creature;

	std::list< t_creature_type >::iterator next_month_creature = creature_types.begin();

	std::advance( next_month_creature, random( creature_types.size() ) );

	m_next_month_creature = *next_month_creature;
}

// --------------------------------------------------------
// adventure map
// get a random artifact of a given level
// --------------------------------------------------------
t_artifact_type t_adventure_map::get_random_artifact( t_artifact_level level )
{
	int             total = 0;
	t_artifact_type type;

	m_used_artifacts.set( k_artifact_parchment );
	m_used_artifacts.set( k_artifact_scroll );
	m_used_artifacts |= ~m_allowed_artifacts;

	for (type = t_artifact_type(0); type < k_artifact_type_count; enum_incr(type))
	{
		if (!is_cut( type ) && get_level( type ) == level && !m_used_artifacts[type])
			total++;
	}

	if (total == 0)
	{
		for (type = t_artifact_type(0); type < k_artifact_type_count; enum_incr(type))
		{
			if (!is_cut( type )
				&& get_level( type ) == level 
				&& type != k_artifact_parchment 
				&& type != k_artifact_scroll
				&& m_allowed_artifacts[type])
			{
				m_used_artifacts.reset( type );
				total++;
			}
		}
	}

	if (total == 0)
	{
		for (type = t_artifact_type(0); type < k_artifact_type_count; enum_incr(type))
		{
			if (!is_cut( type )
				&& get_level( type ) == level 
				&& type != k_artifact_parchment 
				&& type != k_artifact_scroll)
			{
				m_used_artifacts.reset( type );
				total++;
			}
		}
	}

	total = random( total );
	for (type = t_artifact_type(0); type < k_artifact_type_count; enum_incr(type))
	{
		if (!is_cut( type ) && get_level( type ) == level && !m_used_artifacts[type])
		{
			total--;
			if (total < 0)
				break;
		}
	}
	
	m_used_artifacts.set( type );
	m_used_artifacts |= ~m_allowed_artifacts;

	return type;
}

// --------------------------------------------------------
// adventure map
// get a random artifact from a list
// --------------------------------------------------------
t_artifact_type t_adventure_map::get_random_artifact( t_artifact_type const* array,
		                                              int					 count )
{
	int             total = 0;
	int				i;

	m_used_artifacts.set( k_artifact_parchment );
	m_used_artifacts.set( k_artifact_scroll );
	m_used_artifacts |= ~m_allowed_artifacts;

	for (i = 0; i < count; ++i)
	{
		if (!m_used_artifacts[array[i]])
			total++;
	}

	if (total == 0)
	{
		for (i = 0; i < count; ++i)
		{
			if (    array[i] != k_artifact_parchment 
				 && array[i] != k_artifact_scroll
				 && m_allowed_artifacts[array[i]] )
			{
				m_used_artifacts.reset( array[i] );
				total++;
			}
		}
	}

	if (total == 0)
	{
		for (i = 0; i < count; ++i)
		{
			if (    array[i] != k_artifact_parchment 
				 && array[i] != k_artifact_scroll )
			{
				m_used_artifacts.reset( array[i] );
				total++;
			}
		}
	}

	total = random( total );
	for (i = 0; i < count; ++i)
	{
		if (!m_used_artifacts[array[i]])
		{
			total--;
			if (total < 0)
				break;
		}
	}
	m_used_artifacts.set( array[i] );

	m_used_artifacts |= ~m_allowed_artifacts;

	return array[i];
}


// --------------------------------------------------------
// adventure map
// get a random potion - no level needed since all potions are k_artifact_level_items
// --------------------------------------------------------
t_artifact_type t_adventure_map::get_random_potion( )
{
	int             total = 0;
	int				count;
	t_artifact_type	type;

	m_used_artifacts |= ~m_allowed_artifacts;

	for (type = t_artifact_type(0); type < k_artifact_type_count; enum_incr(type))
	{
		if (!is_cut( type ) && !m_used_artifacts[type] && get_slot(type) == k_artifact_slot_potion)
			total++;
	}

	if (total == 0)
	{
		for (type = t_artifact_type(0); type < k_artifact_type_count; enum_incr(type))
		{
			if (!is_cut( type ) && get_slot(type) == k_artifact_slot_potion && m_allowed_artifacts[type])
			{
				m_used_artifacts.reset( type );
				total++;
			}
		}
	}

	if (total == 0)
	{
		for (type = t_artifact_type(0); type < k_artifact_type_count; enum_incr(type))
		{
			if (!is_cut( type ) && get_slot(type) == k_artifact_slot_potion)
			{
				m_used_artifacts.reset( type );
				total++;
			}
		}
	}

	count = total;	

	total = random( count );
	for (type = t_artifact_type(0); type < k_artifact_type_count; enum_incr(type))
	{
		if (!is_cut( type ) && !m_used_artifacts[type] && get_slot(type) == k_artifact_slot_potion)
		{
			total--;
			if (total < 0)
				break;
		}
	}

	m_used_artifacts.set( type );

	m_used_artifacts |= ~m_allowed_artifacts;
	
	return type;
}

// --------------------------------------------------------
// adventure map
// get a random name for one of the 
// --------------------------------------------------------
std::string t_adventure_map::get_random_name( t_qualified_adv_object_type const & type )
{
	int name_list_index = get_random_name_list_index( type );

	if ( name_list_index == -1 )
		return "";

	t_string_vector const & name_list = get_random_name_list( name_list_index );

	// Get list of already-used names
	if ( m_used_random_names.size() <= name_list_index ) 
		m_used_random_names.resize( name_list_index + 1 );

	t_bool_array & used_names = m_used_random_names[ name_list_index ];

	assert( used_names.size() == 0 || used_names.size() == name_list.size() );
	if ( used_names.size() < name_list.size() ) 
		used_names.resize( name_list.size(), false );

	int possible_count = 0;
	t_bool_array::iterator it_bool;

	for ( it_bool = used_names.begin(); it_bool != used_names.end(); it_bool++ )
	{
		if ( !( *it_bool ) ) 
			possible_count++;
	}

	if ( possible_count == 0 )
	{
		// Reset & start over
		for ( it_bool = used_names.begin(); it_bool != used_names.end(); it_bool++ )
		{
			*it_bool = false;
		}

		possible_count = used_names.size();
	}

	int index = random( 1, possible_count );
	t_string_vector::const_iterator result = name_list.begin();

	for ( it_bool = used_names.begin(); it_bool != used_names.end(); it_bool++, result++ )
	{
		if ( !( *it_bool ) ) 
		{
			index--;

			if ( index == 0 )
			{
				*it_bool = true;
				return *result;
			}
		}
	}

	assert( false );
	return get_string( type, "name" );
}


// --------------------------------------------------------
// adventure map
// get a random parchment
// --------------------------------------------------------
t_spell t_adventure_map::get_random_parchment( int level )
{
	int			total = 0;
	t_spell_set	all_spells;
	t_spell		spell;
	t_spell_set spells;

	for (spell = t_spell(0); spell < k_spell_count; enum_incr( spell ))
	{
		if ( get_spell_level( spell ) == level && is_teachable( spell ))
			all_spells[spell] = true;
			
	}
	
	spells = all_spells;
	spells &= get_allowed_spells();
	if (spells.none())
		spells = all_spells;
	spells = select_spells( spells );
	total = spells.count();
	total = random( total );
	for (spell = t_spell(0); spell < k_spell_count; enum_incr( spell ))
	{
		if (spells[spell])
		{
			total--;
			if (total < 0)
				break;
		}
	}
	increment_spell_count( spell );
	return spell;
}

// --------------------------------------------------------
// adventure map
// get a random artifact
// --------------------------------------------------------
void t_adventure_map::reuse( t_artifact_type artifact )
{
	if (artifact != k_artifact_parchment && artifact != k_artifact_scroll && m_allowed_artifacts[artifact])
		m_used_artifacts.reset( artifact );
}

// --------------------------------------------------------
// information about a hero
// --------------------------------------------------------
t_hero* t_adventure_map::create_hero( t_default_hero const& data, t_town_type alignment ) const
{
	t_hero* hero = new t_hero( data, alignment );

	m_default_heroes.use( data.biography_id, data.portrait_id );
	hero->process_new_day( 0, false, NULL, 0 );
	return hero;
}


int t_adventure_map::get_player_number( t_player_color color ) const
{
	int i;

	i = m_players.size();
	while (i--)
		if (m_players[i]->get_color() == color)
			break;
	return i;
}

// --------------------------------------------------------
// Get the number of the given player color
// If no player exists with that color, add it in!
// --------------------------------------------------------
int t_adventure_map::get_or_add_player_number( t_player_color color )
{
	assert( color >= 0 && color < k_active_player_color_count );

	int i;
	int max_team = 0;

	i = m_players.size();
	while (i--)
	{
		if (m_players[i]->get_color() == color)
		{
			return i;
		}

		if ( m_players[i]->get_team() > max_team )
			max_team = m_players[i]->get_team();
	}

	// Add a new player
	t_player_ptr new_player_ptr( new t_player( true, max_team + 1 ) );

	new_player_ptr->set_color( color );
	new_player_ptr->set_name( k_text_computer_player );

	m_players.push_back( new_player_ptr );

	assert( m_players.size() <= k_active_player_color_count );

	return m_players.size() - 1;
}

// --------------------------------------------------------
// check if we have multiple players on this machine
// --------------------------------------------------------
bool t_adventure_map::is_hotseat() const
{
	int i;

	for (i = 0; i < m_players.size(); ++i)
		if (!m_players[i]->is_computer())
			break;
	for (++i; i < m_players.size(); ++i)
		if (!m_players[i]->is_computer())
			return true;
	return false;
}

// --------------------------------------------------------
// add an army
// --------------------------------------------------------
void t_adventure_map::add( t_army* army )
{
	m_armies.push_back( army );
}

// --------------------------------------------------------
// remove an army
// --------------------------------------------------------
void t_adventure_map::remove( t_army* army )
{
	t_army_ptr army_ptr = army;
	assert( std::find( m_armies.begin(), m_armies.end(), army_ptr ) != m_armies.end() );
	m_armies.remove( army_ptr );
}


// --------------------------------------------------------
// add a garrison
// --------------------------------------------------------
void t_adventure_map::add( t_ownable_garrisonable_adv_object* garrison )
{
	m_garrisons.push_back( garrison );
}

// --------------------------------------------------------
// remove an garrison
// --------------------------------------------------------
void t_adventure_map::remove( t_ownable_garrisonable_adv_object* garrison )
{
	t_ownable_garrisonable_adv_object_ptr garrison_ptr = garrison;
	assert( std::find( m_garrisons.begin(), m_garrisons.end(), garrison_ptr ) != m_garrisons.end() );
	m_garrisons.remove( garrison_ptr );
}

// --------------------------------------------------------
// add a town
// --------------------------------------------------------
void t_adventure_map::add( t_town* town )
{
	m_towns.push_back( town );
}

// --------------------------------------------------------
// remove a town
// --------------------------------------------------------
void t_adventure_map::remove( t_town* town )
{
	t_town_ptr town_ptr = town;
	assert( std::find( m_towns.begin(), m_towns.end(), town_ptr ) != m_towns.end() );
	m_towns.remove( town_ptr );
}

// --------------------------------------------------------
// get decimal code for a transition
// --------------------------------------------------------
inline int get_transition_code( t_terrain_transition const& transition )
{
	return g_transition_masks[ transition.get_mask_set() ][ transition.get_id() ].get_code();
}

static int sum_land_transitions( t_adventure_tile const& tile )
{
	std::vector< t_terrain_transition > const&	transitions 
		= tile.get_terrain_transition_vector();
	t_terrain_type								terrain;

	int		i;
	int		bits = 0;
	int		decimal_code;
	int		binary_code;

	i = transitions.size();
	while (i--)
	{
		terrain = transitions[i].get_terrain();
		if (terrain == k_terrain_water || terrain == k_terrain_water_river)
			continue;
		decimal_code = get_transition_code( transitions[i] );
		binary_code = translate_transition_code( decimal_code );
		if (binary_code > 255) // ignore corner transitions
			continue;
		// add all land transitions
		bits |= binary_code;
	}
	return bits;
}

// --------------------------------------------------------
// return whether a given square is considered "ocean"
// --------------------------------------------------------
bool t_adventure_map::is_ocean( t_level_map_point_2d const& point ) const
{
	return !is_land( point );
}

// --------------------------------------------------------
// return whether a given square is considered "river"
// --------------------------------------------------------
bool t_adventure_map::is_river( t_level_map_point_2d const& point ) const
{
	t_adventure_tile const& tile = m_map.get( point );
	t_terrain_type          terrain = tile.get_terrain();

	if (terrain != k_terrain_water && terrain != k_terrain_water_river)
		return false;

	return is_river_transition_code( sum_land_transitions( tile ) );
}

// --------------------------------------------------------
// return whether a given square is considered "land"
// --------------------------------------------------------
bool t_adventure_map::is_land( t_level_map_point_2d const& point ) const
{
	t_adventure_tile const& tile = m_map.get( point );
	t_terrain_type          terrain = tile.get_terrain();

	if (terrain != k_terrain_water && terrain != k_terrain_water_river)
		return true;

	return is_land_transition_code( sum_land_transitions( tile ) );
}


// ---------------------------------------------------------------
// Add ferry to list.
// ---------------------------------------------------------------
void t_adventure_map::add_ferry( t_adv_ferry_const_ptr const & ferry )
{
	// Add to main list
	t_ferry_group_id_map::value_type val( ferry, 0 );
	std::pair< t_ferry_group_id_map::iterator, bool > rtn = m_ferries.insert( val );

	assert( rtn.second );

	m_ferries_by_group.clear();
	m_ferry_groups_calculated = false;
}

// ---------------------------------------------------------------
// Helper for calculate_ferry_groups: add point into open_list
// and listed_points.
// ---------------------------------------------------------------
void t_adventure_map::calculate_ferry_groups_push( t_level_map_point_2d const & point,
												   t_level_map_point_2d_list & open_list,
												   t_bool_array & listed_points ) const
{
	if ( is_valid( point )  )
	{
		int listing_index = ( point.row * get_size() ) + point.column;

		assert( listing_index < listed_points.size() );

		if ( !listed_points[ listing_index ] )
		{
			listed_points[ listing_index ] = true;

			if ( is_ocean( point ) )
				open_list.push_back( point );
		}
	}
}

// ---------------------------------------------------------------
// Helper for calculate_ferry_groups: add ferry into open_list
// and listed_points, and the ferry group
// ---------------------------------------------------------------
void t_adventure_map::calculate_ferry_groups_add_ferry( t_adv_ferry const * ferry,
														t_level_map_point_2d_list & open_list,
														t_bool_array & listed_points,
														int group_id,
											  			t_adv_ferry_const_vector & group_vector,
														t_ferry_points_map const & ferry_points_map ) const
{
	t_ferry_group_id_map::iterator ferry_group_id_map_iterator = m_ferries.find( ferry );

	assert( ferry_group_id_map_iterator != m_ferries.end() );

	if ( ferry_group_id_map_iterator->second == -1 )
	{
		ferry_group_id_map_iterator->second = group_id;
		group_vector.push_back( ferry );

		t_level_map_point_2d_list const & point_list = ferry_points_map.find( ferry )->second;
		t_level_map_point_2d_list::const_iterator it;
		for ( it = point_list.begin(); it != point_list.end(); it++ )
		{
			calculate_ferry_groups_push( *it, open_list, listed_points );
		}
	}
	else
	{
		assert( ferry_group_id_map_iterator->second == group_id ); // One group id per ferry!
	}
}


// ---------------------------------------------------------------
// Calculate the ferry groups; that is, determine which set of 
// ferries are on contiguous bodies of water
// ---------------------------------------------------------------
void t_adventure_map::calculate_ferry_groups() const
{
	m_ferries_by_group.clear();

	// A mapping of ferries to the initial points they can reach ( points that
	// touch their blocked square )
	t_ferry_points_map ferry_points_map;

	// A mapping of all the ferries that can reach the given point ( points
	// that touch the blocked square of the ferry )
	t_point_ferry_map point_ferry_map;

	// Set up maps
	t_ferry_group_id_map::iterator ferry_iterator;

	for ( ferry_iterator = m_ferries.begin(); ferry_iterator != m_ferries.end(); ferry_iterator++ )
	{
		ferry_iterator->second = -1;

		// Find initial points. Anything within 1 square of the ferry's blocked point
		// is OK
		t_level_map_point_2d ferry_origin = ferry_iterator->first->get_position();
		t_footprint const &  ferry_footprint = ferry_iterator->first->get_footprint();
		t_map_point_2d       offset;
		t_map_point_2d       footprint_size = ferry_footprint.get_size();
		t_level_map_point_2d_list point_list;

		for ( offset.row = 0; offset.row < footprint_size.row; offset.row++ )
		{
			for ( offset.column = 0; offset.column < footprint_size.column; offset.column++ )
			{
				if ( ferry_footprint.is_cell_impassable( offset ) )
				{
					t_level_map_point_2d blocked_point = ferry_origin + offset;

					if ( is_valid( blocked_point ) && is_ocean( blocked_point ) )
						point_list.push_back( blocked_point );

					t_direction direction;

					for ( direction = t_direction(0); direction < k_direction_count; enum_incr( direction ) )
					{
						t_level_map_point_2d nearby_point = blocked_point + get_direction_offset( direction );

						if ( is_valid( nearby_point ) && is_ocean( nearby_point ) )
							point_list.push_back( nearby_point );
					}
				}
			}
		}
		
		std::pair< t_ferry_points_map::iterator, bool > rtn = 
			ferry_points_map.insert( t_ferry_points_map::value_type( ferry_iterator->first.get(), point_list ) );

		assert( rtn.second );

		t_level_map_point_2d_list::iterator point_iterator;
		for ( point_iterator = point_list.begin(); point_iterator != point_list.end(); point_iterator++ )
		{
			point_ferry_map.insert( t_point_ferry_map::value_type( *point_iterator, ferry_iterator->first.get() ) );
		}
	}

	// Run through ferries and assign to groups
	int group_id = 0;
	for ( ferry_iterator = m_ferries.begin(); ferry_iterator != m_ferries.end(); ferry_iterator++ )
	{
		if ( ferry_iterator->second != -1 )
			continue; // already dealt with

		// Group we are adding the ferries to.
		t_adv_ferry_const_vector group_vector;

		// Run a flood fill out from this ferry across the water
		// We could do this using t_adventure_path_finder, but since we don't care about
		// blocking objects and don't want to create an army, it's simplier to just
		// do it ourselves
		int const map_size = get_size();
		t_bool_array listed_points; // Points already processed
		listed_points.resize( square( map_size ), false );

		t_level_map_point_2d_list open_list;

		calculate_ferry_groups_add_ferry( ferry_iterator->first.get(), open_list, listed_points, group_id, group_vector, ferry_points_map );

		while ( !open_list.empty() )
		{
			t_level_map_point_2d point = open_list.front();
			open_list.pop_front();

			assert( is_valid( point ) && is_ocean( point ) );

			if ( !is_valid( point ) || !is_ocean( point ) )
				continue;

			t_point_ferry_map::iterator point_ferry_iterator = point_ferry_map.find( point );

			while ( point_ferry_iterator != point_ferry_map.end() && point_ferry_iterator->first == point )
			{
				calculate_ferry_groups_add_ferry(  point_ferry_iterator->second, open_list, listed_points, group_id, group_vector, ferry_points_map );

				point_ferry_iterator++;
			}

			// Add nearby points to open list
			t_direction direction;

			for ( direction = t_direction(0); direction < k_direction_count; enum_incr( direction ) )
			{
				t_level_map_point_2d nearby_point = point + get_direction_offset( direction );
				calculate_ferry_groups_push( nearby_point, open_list, listed_points );
			}
		}


		assert( !group_vector.empty() );
		m_ferries_by_group.push_back( group_vector );

		group_id++;

		assert( m_ferries_by_group.size() == group_id );

	}

	m_ferry_groups_calculated = true;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::add_gateway( t_gateway_ptr const & gateway )
{
	int gateway_type = gateway->get_gateway_type();

	assert( gateway_type >= 0 && gateway_type < k_gateway_type_count );

	if ( gateway_type >= 0 && gateway_type < k_gateway_type_count )
	{
#ifndef NDEBUG
		t_gateway_vector::const_iterator it;
		for ( it = m_gateways[gateway_type].begin(); it != m_gateways[gateway_type].end(); it++ )
		{
			assert( gateway != *it );
		}
#endif
	    m_gateways[gateway_type].push_back( gateway );
	}
}


// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::add_magi_eye( t_adv_magi_eye& magi_eye )
{
    m_magi_eyes.push_back( magi_eye );
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::add_subterranean_gate( t_adv_subterranean_gate_ptr gate )
{
#ifndef NDEBUG
	t_adv_subterranean_gate_vector::const_iterator it;
	for ( it = m_subterranean_gates.begin(); it != m_subterranean_gates.end(); it++ )
	{
		assert( gate != *it );
	}
#endif
    m_subterranean_gates.push_back( gate );
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::add_teleporter_exit( t_adv_teleporter_exit_ptr const & exit )
{
	int teleporter_type = exit->get_teleporter_type();

	assert( teleporter_type >= 0 && teleporter_type < k_gateway_type_count );

	if ( teleporter_type >= 0 && teleporter_type < k_gateway_type_count )
	{
#ifndef NDEBUG
		t_adv_teleporter_exit_vector::const_iterator it;
		for ( it = m_teleporter_exits[teleporter_type].begin(); it != m_teleporter_exits[teleporter_type].end(); it++ )
		{
			assert( exit != *it );
		}
#endif
	    m_teleporter_exits[teleporter_type].push_back( exit );
	}
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::add_whirlpool( t_adv_whirlpool_ptr const & whirlpool )
{
#ifndef NDEBUG
	t_adv_whirlpool_vector::const_iterator it;
	for ( it = m_whirlpools.begin(); it != m_whirlpools.end(); it++ )
	{
		assert( whirlpool != *it );
	}
#endif
    m_whirlpools.push_back( whirlpool );
}

// ---------------------------------------------------------------
// Get all ferries
// ---------------------------------------------------------------
t_adv_ferry_const_vector t_adventure_map::get_ferries() const
{
	t_adv_ferry_const_vector result;
    
	result.reserve( m_ferries.size() );

	t_ferry_group_id_map::const_iterator it;

	for ( it = m_ferries.begin(); it != m_ferries.end(); it++ )
	{
		result.push_back( it->first );
	}

    return result;
}

// ---------------------------------------------------------------
// Get all the ferries that can be reached by the given ferry
// ---------------------------------------------------------------
t_adv_ferry_const_vector t_adventure_map::get_ferries_in_group( t_adv_ferry_const_ptr ferry ) const
{
	if ( !m_ferry_groups_calculated )
		calculate_ferry_groups();

	t_ferry_group_id_map::const_iterator ferry_data = m_ferries.find( ferry );

	assert( ferry_data != m_ferries.end() );

	if ( ferry_data == m_ferries.end() )
	{
		t_adv_ferry_const_vector result;
		result.push_back( ferry );
		return result;
	}

	assert( ferry_data->second >= 0 && ferry_data->second < m_ferries_by_group.size() );

	if ( ferry_data->second >= 0 && ferry_data->second < m_ferries_by_group.size() )
	{
		return m_ferries_by_group[ ferry_data->second ];
	}
	else
	{
		t_adv_ferry_const_vector result;
		result.push_back( ferry );
		return result;
	}
}

// ---------------------------------------------------------------
// Get all the teleporting gateways of the given type
// ---------------------------------------------------------------
t_gateway_vector t_adventure_map::get_gateways( int gateway_type )
{
	assert( gateway_type >= 0 && gateway_type < k_gateway_type_count );

	if ( gateway_type >= 0 && gateway_type < k_gateway_type_count )
	{
		return m_gateways[ gateway_type ];
	}
	else
	{
		return t_gateway_vector();
	}
}

// ---------------------------------------------------------------
// Get all the teleporting gateways of the given type
// --------------------------------------------------
t_gateway_const_vector t_adventure_map::get_gateways( int gateway_type ) const
{
	t_gateway_const_vector result;

	assert( gateway_type >= 0 && gateway_type < k_gateway_type_count );

	if ( gateway_type >= 0 && gateway_type < k_gateway_type_count )
	{
		result.reserve( m_gateways[ gateway_type ].size() );

		t_gateway_vector::const_iterator it;

		for ( it = m_gateways[ gateway_type ].begin(); it != m_gateways[ gateway_type ].end(); it++ )
			result.push_back( t_gateway_const_ptr( *it ) );
	}

	return result;
}

// ---------------------------------------------------------------
// Get all the teleporter exits of the given type
// ---------------------------------------------------------------
t_adv_teleporter_exit_vector t_adventure_map::get_teleporter_exits( int exit_type )
{
	assert( exit_type >= 0 && exit_type < k_gateway_type_count );

	if ( exit_type >= 0 && exit_type < k_gateway_type_count )
	{
		return m_teleporter_exits[ exit_type ];
	}
	else
	{
		return t_adv_teleporter_exit_vector();
	}
}

// ---------------------------------------------------------------
// Get all the teleporting gateways of the given type
// ---------------------------------------------------------------
t_adv_teleporter_exit_const_vector t_adventure_map::get_teleporter_exits( int exit_type ) const
{
	t_adv_teleporter_exit_const_vector result;

	assert( exit_type >= 0 && exit_type < k_gateway_type_count );

	if ( exit_type >= 0 && exit_type < k_gateway_type_count )
	{
		result.reserve( m_teleporter_exits[ exit_type ].size() );

		t_adv_teleporter_exit_vector::const_iterator it;

		for ( it = m_teleporter_exits[ exit_type ].begin(); it != m_teleporter_exits[ exit_type ].end(); it++ )
			result.push_back( t_adv_teleporter_exit_const_ptr( *it ) );
	}

	return result;
}

// ---------------------------------------------------------------
// Return all the whirlpools on the map
// ---------------------------------------------------------------
t_adv_whirlpool_vector t_adventure_map::get_whirlpools()
{
	return m_whirlpools;
}

// ---------------------------------------------------------------
// Return all the whirlpools on the map
// ---------------------------------------------------------------
t_adv_whirlpool_const_vector t_adventure_map::get_whirlpools() const
{
	t_adv_whirlpool_const_vector result;
	result.reserve( m_whirlpools.size() );

	t_adv_whirlpool_vector::const_iterator it;

	for ( it = m_whirlpools.begin(); it != m_whirlpools.end(); it++ )
	{
		result.push_back( t_adv_whirlpool_const_ptr( *it ) );
	}

	return result;
}

// ---------------------------------------------------------------
// Remove from the list of magic ferries
// ---------------------------------------------------------------
void t_adventure_map::remove_ferry( t_adv_ferry_const_ptr const & ferry )
{
	int remove_count = m_ferries.erase( ferry );

	assert( remove_count == 1 );

	m_ferries_by_group.clear();
	m_ferry_groups_calculated = false;
}

// ---------------------------------------------------------------
// Remove from the list of teleporting gateways
// ---------------------------------------------------------------
void t_adventure_map::remove_gateway( t_gateway_ptr const & gateway )
{
	int gateway_type = gateway->get_gateway_type();

	assert( gateway_type >= 0 && gateway_type < k_gateway_type_count );

	if ( gateway_type >= 0 && gateway_type < k_gateway_type_count )
	{
		t_gateway_vector::iterator it;
		for ( it = m_gateways[gateway_type].begin(); it != m_gateways[gateway_type].end(); it++ )
		{
			if ( gateway == *it )
			{
				m_gateways[gateway_type].erase( it );
				return;
			}
		}

		assert( false );
	}
}


// ---------------------------------------------------------------
// Remove from the list of subterranean gateways
// ---------------------------------------------------------------
void t_adventure_map::remove_subterranean_gate( t_adv_subterranean_gate_ptr const & gate )
{
	t_adv_subterranean_gate_vector::iterator it;
	for ( it = m_subterranean_gates.begin(); it != m_subterranean_gates.end(); it++ )
	{
		if ( gate == *it )
		{
			m_subterranean_gates.erase( it );
			return;
		}
	}

	assert( false );
}

// ---------------------------------------------------------------
// Remove from the list of teleporter exits
// ---------------------------------------------------------------
void t_adventure_map::remove_teleporter_exit( t_adv_teleporter_exit_ptr const & exit )
{
	int teleporter_type = exit->get_teleporter_type();

	assert( teleporter_type >= 0 && teleporter_type < k_gateway_type_count );

	if ( teleporter_type >= 0 && teleporter_type < k_gateway_type_count )
	{
		t_adv_teleporter_exit_vector::iterator it;
		for ( it = m_teleporter_exits[teleporter_type].begin(); it != m_teleporter_exits[teleporter_type].end(); it++ )
		{
			if ( exit == *it )
			{
				m_teleporter_exits[teleporter_type].erase( it );
				return;
			}
		}

		assert( false );
	}
}


// ---------------------------------------------------------------
// Remove from the list of whirlpools
// ---------------------------------------------------------------
void t_adventure_map::remove_whirlpool( t_adv_whirlpool_ptr const & whirlpool )
{
	t_adv_whirlpool_vector::iterator it;
	for ( it = m_whirlpools.begin(); it != m_whirlpools.end(); it++ )
	{
		if ( whirlpool == *it )
		{
			m_whirlpools.erase( it );
			return;
		}
	}

	assert( false );
}

void t_adventure_map::add_to_total_income ( int amount )
{
	m_total_map_income += amount;
}
        
void t_adventure_map::add_obelisk( t_adv_obelisk& obelisk )
{
    m_obelisks.push_back( obelisk );
    m_obelisk_count[ obelisk.get_major_subtype() ]++;
}

// Get artifact position.
t_level_map_point_2d t_adventure_map::get_obelisk_artifact_position(  t_obelisk_color obelisk_type ) const
{
    return m_obelisk_artifact_position[ obelisk_type ];
}

// Set artifact position.
void t_adventure_map::set_obelisk_artifact_position( t_obelisk_color obelisk_type, t_level_map_point_2d const& position )
{
    m_obelisk_artifact_position[ obelisk_type ] = position;
}

// --------------------------------------------------------
// --------------------------------------------------------
bool t_adventure_map::write_events( std::streambuf & stream ) const
{
	put< t_uint16 > (stream, t_abstract_script_action::get_version());	

	int count = m_timed_events.size();
	put< t_uint16 >( stream, count );
	int i;
	for (i = 0; i < count; i++) {
		if (m_timed_events[i]->write( stream ) == false) 
			return false;
	}

	count = m_triggerable_events.size();
	put< t_uint16 >( stream, count );
	for (i = 0; i < count; i++) {
		if (m_triggerable_events[i]->write( stream ) == false) 
			return false;
	}

	count = m_continuous_events.size();
	put< t_uint16 >( stream, count );
	for (i = 0; i < count; i++) {
		if (m_continuous_events[i]->write( stream ) == false) 
			return false;
	}

	count = m_placed_events.size();
	put< t_uint16 >( stream, count );
	for (i = 0; i < count; i++) {
		if (m_placed_events[i]->write( stream ) == false) 
			return false;
	}

 	return true;
}

// --------------------------------------------------------
// --------------------------------------------------------
bool t_adventure_map::read_events( std::streambuf & stream, int version)
{
	if ( version < 6 )
		return true;

	t_timed_event_list new_timed_events;
	t_triggerable_event_list new_triggerable_events;
	t_continuous_event_list new_continuous_events;
	t_placed_event_list new_placed_events;

	int script_version = get< t_uint16 >(stream);

	int count = get< t_uint16 >( stream );
	new_timed_events.reserve( count );

	while ( count > 0 )
	{
		t_timed_event_ptr new_event_ptr( new t_timed_event );
		bool read_result;
		if ( version < 8 )
			read_result = read_old_timed_event( stream, version, *new_event_ptr );
		else
			read_result = new_event_ptr->read( stream, script_version );
		if ( !read_result )
			return false;

		new_timed_events.push_back( new_event_ptr);

		--count;
	}

	count = get< t_uint16 >( stream );
	if ( version < 8 )
	{
		while ( count > 0 )
		{
			std::string name;
			if ( !read_string16( stream, name ) )
				return false;

			bool continuous = get< bool >( stream );

			if ( version >= 1 )
			{
				get< bool >( stream ); // discard "currrent player only" flag
				get< bool >( stream ); // discard "execution" flag
			}

			t_script_event::t_script_ptr new_script_ptr =
				t_script_event::t_script::reconstruct( stream, version );
			if ( new_script_ptr.get() == 0 )
				return false;

			if ( continuous )
			{
				t_continuous_event_ptr new_event_ptr( new t_continuous_event );
				new_event_ptr->set_script( new_script_ptr );
				new_continuous_events.push_back( new_event_ptr );
			}
			else
			{
				t_triggerable_event_ptr new_event_ptr( new t_triggerable_event );
				new_event_ptr->set_name( name );
				new_event_ptr->set_script( new_script_ptr );
				new_triggerable_events.push_back( new_event_ptr );
			}

			--count;
		}
	}
	else
	{
		m_triggerable_events.reserve( count );

		while ( count > 0 )
		{
			t_triggerable_event_ptr new_event_ptr( new t_triggerable_event );
			if ( !new_event_ptr->read( stream, script_version ) )
				return false;

			new_triggerable_events.push_back( new_event_ptr );

			--count;
		}

		count = get< t_uint16 >( stream );
		m_continuous_events.reserve( count );

		while ( count > 0 )
		{
			t_continuous_event_ptr new_event_ptr( new t_continuous_event );
			if ( !new_event_ptr->read( stream, script_version ) )
				return false;

			new_continuous_events.push_back( new_event_ptr );

			--count;
		}

		if (version > 18)
		{
			count = get< t_uint16 >( stream );
			m_placed_events.reserve( count );

			while ( count > 0 )
			{
				t_placed_event_ptr new_event_ptr( new t_placed_event );
				if ( !new_event_ptr->read( stream, script_version ) )
					return false;

				new_placed_events.push_back( new_event_ptr );

				--count;
			}
		}

	}

	m_timed_events.swap( new_timed_events );
	m_triggerable_events.swap( new_triggerable_events );
	m_continuous_events.swap( new_continuous_events );
	m_placed_events.swap( new_placed_events );

	return true;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
t_adv_subterranean_gate_vector const &  t_adventure_map::get_subterranean_gates( )
{
	return m_subterranean_gates;
}


t_obelisk_list t_adventure_map::get_obelisk_list( int obelisk_type )
{
	t_obelisk_list result;
    int i;

	for ( i = 0; i < m_obelisks.size(); i++)
	{
        // If -1, get all subtypes.
	    if ( obelisk_type == -1 )
		    result.push_back( m_obelisks[i] );
        else if ( m_obelisks[i].get_major_subtype() == obelisk_type )
		    result.push_back( m_obelisks[i] );
    }
    return (result);
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::set_boolean_script_variable( std::string const& name, bool value )
{
	t_boolean_variable_map::iterator iter = m_map_boolean_variables.find( name );
	if (iter == m_map_boolean_variables.end()) 
		m_map_boolean_variables.insert(t_boolean_variable_map::value_type(name, value));
	else
		(*iter).second = value;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::set_numeric_script_variable( std::string const& name, int value )
{
	t_numeric_variable_map::iterator iter = m_map_numeric_variables.find( name );
	if (iter == m_map_numeric_variables.end()) 
		m_map_numeric_variables.insert(t_numeric_variable_map::value_type(name, value));
	else
		(*iter).second = value;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::get_numeric_script_variable( std::string const& name, int& value) const
{
	t_numeric_variable_map::const_iterator iter = m_map_numeric_variables.find( name );
	if (iter == m_map_numeric_variables.end()) 
		return false;

	value = (*iter).second;
	return true;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::get_boolean_script_variable( std::string const& name, bool& value) const
{
	t_boolean_variable_map::const_iterator iter = m_map_boolean_variables.find( name );
	if (iter == m_map_boolean_variables.end()) 
		return false;

	value = (*iter).second;
	return true;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::process_timed_events( t_adventure_frame* frame )
{
	int size = m_timed_events.size();
	for (int i = 0; i < size; i++) {
		t_timed_event_ptr & event = m_timed_events[i];
		if ( event->is_active_on_day(get_day()) && event->ownership_test(&get_current_player())) {
			event->execute_effects(get_current_player(), frame);
			frame->update();

			t_timed_event::t_script const & script = event->get_script();
			script.execute(t_script_context_global(this));
			if (is_game_over())
				return;
			frame->update();

			if (script.pending_removal()) {
				script.clear_removal();
				event = new t_global_timed_event;
			}
		}
	}

	for ( int object_id = get_first_object_id(); object_id != k_invalid_object_id; ) 
	{
		t_adventure_object_ptr object = &get_adv_object( object_id );

		// Get the next object id now because processing events may cause this
		// object to be removed from the map
		object_id = get_next_object_id( object_id );

		if ( object->process_timed_events( *this ) )
			object->update_state();
	}

	process_continuous_events( frame );
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::process_continuous_events( t_adventure_frame* frame )
{
	int size = m_continuous_events.size();
	for (int i = 0; i < size; i++) {
		t_continuous_event_ptr & event = m_continuous_events[i];

		t_continuous_event::t_script const & script = event->get_script();
		script.execute(t_script_context_global(this));
		if (is_game_over())
			return;
		frame->update();

		if (script.pending_removal()) {
			script.clear_removal();
			event = new t_global_continuous_event;
		}
	}

	for ( int object_id = get_first_object_id(); object_id != k_invalid_object_id; ) 
	{
		t_adventure_object_ptr object = &get_adv_object( object_id );

		// Get the next object id now because processing events may cause this
		// object to be removed from the map
		object_id = get_next_object_id( object_id );

		if ( object->process_continuous_events( *this ) )
			object->update_state();
	}
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::process_triggerable_events(std::string const& name, t_adventure_frame* frame )
{
	int size = m_triggerable_events.size();
	for (int i = 0; i < size; i++) {
		t_triggerable_event_ptr & event = m_triggerable_events[i];
		if ( string_insensitive_compare(event->get_name(), name) == 0) {
			event->execute_effects(get_current_player(), frame);
			if (frame) 
				frame->update();

			t_triggerable_event::t_script const & script = event->get_script();
			script.execute(t_script_context_global(this));
			if (is_game_over())
				return;
			if (frame) 
				frame->update();

			if (script.pending_removal()) {
				script.clear_removal();
				event = new t_global_triggerable_event;
			}
		}
	}

	for ( int object_id = get_first_object_id(); object_id != k_invalid_object_id; ) 
	{
		t_adventure_object_ptr object = &get_adv_object( object_id );

		// Get the next object id now because processing events may cause this
		// object to be removed from the map
		object_id = get_next_object_id( object_id );

		if ( object->process_triggerable_events( *this, name ) )
			object->update_state();
	}
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::process_placed_events(std::string const& name, t_army& army, t_adventure_object_ptr object )
{
	int size = m_placed_events.size();
	for (int i = 0; i < size; i++) {
		t_placed_event_ptr & event = m_placed_events[i];
		if ( string_insensitive_compare(event->get_name(), name) == 0) {
			if ( event->get_message().size() != 0 )
				get_map_window()->halt_movement();

			t_adventure_frame* frame = army.get_adventure_frame();
			event->execute_effects(*army.get_owner(), frame);
			frame->update();

			t_triggerable_event::t_script const & script = event->get_script();

			script.execute(
				t_script_context_army(this, &army.get_creatures(), army.get_owner(), NULL, NULL, &army.get_position(), object.get()) );
			
			if (is_game_over())
				return;

			frame->update();
			post_execute_validate(&army);

			if (script.pending_removal()) {
				script.clear_removal();
				event = new t_global_placed_event;
			}

			process_continuous_events(frame);
		}
	}
}


// ---------------------------------------------------------------
// check if this square is open and a legal place to put a boat, if
// unblocked.  Does not check if a boat is already in the square
// ---------------------------------------------------------------
bool t_adventure_map::is_permanently_blocked( t_adv_map_point const& point ) const
{
	 // if it's blocked, check if there are only removable objects blocking it.
	t_adventure_tile const& tile = m_map.get(point);

	if (!tile.is_blocked( point.on_bridge ))
		return false;

	if (!tile.is_trigger( point.on_bridge ))
		return true;

	const std::vector<int>& triggers = tile.get_triggers(point.on_bridge);
	for (std::vector<int>::const_iterator index = triggers.begin(); index != triggers.end(); ++index)
		if (!get_adv_object( *index ).is_removable())
			return true;
	return false;
}

// ---------------------------------------------------------------
// check if this square is open and a legal place to put an empty 
// boat.  Must be on water, next to land and not already occupied.
// ---------------------------------------------------------------
bool t_adventure_map::can_place_new_boat( t_adv_map_point const& center, bool ignore_removable ) const
{
	bool					have_adjacent_land = false;
	bool					blocked[k_direction_count];
	t_direction				direction;

	// See if the target point itself is available
	if (!is_valid( center ) || !is_ocean( center ))
		return false;

	if ( center.on_bridge ) // Cannot place boats on bridges
		return false;

	if ( ignore_removable ? is_permanently_blocked( center ) :  is_blocked( center ) )
		return false;

	if ( !ignore_removable )
	{
		// See if there's already somebody else here
		t_adventure_tile const& tile = get_adv_tile( center );
		
		const std::vector<int>& triggers = tile.get_triggers(false);
		std::vector<int>::const_iterator it = triggers.begin();
		while (it != triggers.end())
		{
			t_adventure_object const & object = get_adv_object( *it );
			if (object.is_actor())
				return false;
			it++;
		}
	}

	// find which directions are blocked
	for (direction = t_direction(0); direction < k_direction_count; enum_incr( direction ))
	{
		t_adv_map_point point = center + get_direction_offset( direction );

		if (!is_valid( point ))
		{
			blocked[direction] = true;
		}
		if (is_land( point ))
		{
			blocked[direction] = true;
			have_adjacent_land = true;
		}
		else
		{
			blocked[direction] = ignore_removable ? is_permanently_blocked( point ) : is_blocked( point );
		}
	}

	// If we don't have any adjacent land, dump out now (because we don't want empty ships created
	// adrift at sea)
	// NOTE:  Remove this condition if you DO want ships created at sea.  This would probably accompany
	// a change to make ships at towns created only in cells that can be "visiting armies" so that troops
	// can move directly into the ship.
	if (!have_adjacent_land)
		return false;

	// for each orthagonal direction, block the diagonal directions if it is blocked.
	for (direction = k_direction_northeast; direction < k_direction_count; enum_incr( direction, 2 ))
	{
		if (!blocked[direction])
			continue;

		blocked[clockwise( direction )] = true;
		blocked[counter_clockwise( direction )] = true;
	}
	// check if any unblocked direction remains and we have adjacent land.
	for (direction = t_direction(0); direction < k_direction_count; enum_incr( direction ))
	{
		if (blocked[direction])
			continue;
		return true;
	}

	return false;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::write_script_variable_maps( std::streambuf& stream ) const
{
	put< t_uint16 >( stream, m_map_numeric_variables.size() );
	t_numeric_variable_map::const_iterator numeric_map_iter = m_map_numeric_variables.begin();
	for ( ; numeric_map_iter != m_map_numeric_variables.end(); ++numeric_map_iter )
	{
		if ( !write_string16( stream, numeric_map_iter->first ) ) 
			return false;
			
		put< t_int16 >( stream, numeric_map_iter->second );
	}

	put< t_uint16 >( stream, m_map_boolean_variables.size() );
	t_boolean_variable_map::const_iterator boolean_map_iter = m_map_boolean_variables.begin();
	for ( ; boolean_map_iter != m_map_boolean_variables.end(); ++boolean_map_iter )
	{
		if( !write_string16( stream, boolean_map_iter->first ) )
			return false;

		put< bool >( stream, boolean_map_iter->second );
	}
	return true;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::write_deletion_marker_map( std::streambuf& stream ) const
{
	put< t_uint16 >( stream, m_map_deletion_markers.size() );
	t_deletion_marker_map::const_iterator deletion_map_iter = m_map_deletion_markers.begin();
	for ( ; deletion_map_iter != m_map_deletion_markers.end(); ++deletion_map_iter )
	{
		if( !write_string16( stream, deletion_map_iter->first)	)
			return false;

		const t_point_list& points = deletion_map_iter->second;
		put< t_uint16 >( stream, points.size() );
		t_point_list::const_iterator point_iter = points.begin();
		while( point_iter != points.end() ) {
			stream << *point_iter;
			point_iter++;
		}
	}
	return true;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::read_script_variable_maps( std::streambuf& stream, int version )
{
	// Read script variable maps
	if (version > 5) {
		std::string name;

		m_map_numeric_variables.clear();
		int map_size = get < t_uint16 >( stream );
		for (int i = 0; i < map_size; i++) 
		{
			if (!read_string16( stream, name ))
				return false;
			m_map_numeric_variables.insert(t_numeric_variable_map::value_type(name, get< t_int16 >(stream)));
		}

		map_size = get < t_uint16 >( stream );
		for (int i = 0; i < map_size; i++) 
		{
			if (!read_string16( stream, name ))
				return false;
			m_map_boolean_variables.insert(t_boolean_variable_map::value_type(name, get< bool >(stream)));
		}
	}
	return true;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::read_deletion_marker_map( std::streambuf& stream, int version )
{
	if (version > 6) {
		std::string name;

		m_map_deletion_markers.clear();
		int map_size = get < t_uint16 >( stream );
		for (int i = 0; i < map_size; i++) 
		{
			if (!read_string16( stream, name ))
				return false;
			
			t_point_list points;
			int list_size = get< t_uint16 >( stream ); 
			for (int j = 0; j < list_size; j++) 
			{
				t_adv_map_point point;
				stream >> point;
				points.push_back(point);
			}
			m_map_deletion_markers.insert(t_deletion_marker_map::value_type(name, points));
		}
	}

	return true;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::add_deletion_marker( std::string const& name, t_adv_map_point const& point )
{
	t_deletion_marker_map::iterator iter = m_map_deletion_markers.find( name );
	if (iter == m_map_deletion_markers.end()) {
		t_point_list list;
		list.push_back(point);
		m_map_deletion_markers.insert(t_deletion_marker_map::value_type(name, list));
	} else {
		(*iter).second.push_back(point);
	}
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::remove_by_deletion_marker( std::string const& name )
{
	t_deletion_marker_map::iterator iter = m_map_deletion_markers.find( name );
	if (iter == m_map_deletion_markers.end()) 
		return;

	const t_point_list& points = iter->second;

	std::list< t_adventure_object_ptr > destroy_list;

	t_point_list::const_iterator point_iter = points.begin();
	while( point_iter != points.end() ) {
		t_adventure_tile const& new_tile = m_map.get( *point_iter++ );

		int object_cnt = new_tile.get_intersecting_object_count();
		for (int i = 0; i < object_cnt; i++)
		{
			t_stationary_adventure_object* adv_object = 
					dynamic_cast<t_stationary_adventure_object*>(
						&get_object(new_tile.get_intersecting_object_id(i)) );

			if (!adv_object) continue;

			if ( !adv_object->is_deleted_by_deletion_marker() )
				continue;
				
			destroy_list.push_back(adv_object);
		}
	}

	while( !destroy_list.empty() ) {
		if ( destroy_list.front()->is_on_map() ) 
		{
			destroy_list.front()->destroy();
		}
		destroy_list.pop_front();
	}
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::add_mapped_name( std::string const& name, t_hero* hero )
{
	if (m_map_hero_name.find( name ) == m_map_hero_name.end()) 
	{
		m_map_hero_name.insert(t_hero_name_map::value_type(name, hero));
	}
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::add_mapped_name( std::string const& name, t_town* town )
{
	if (m_map_town_name.find( name ) == m_map_town_name.end()) 
	{
		m_map_town_name.insert(t_town_name_map::value_type(name, town));
	}
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
t_hero* t_adventure_map::get_mapped_hero( std::string const& name )
{
	t_hero_name_map::iterator iter = m_map_hero_name.find( name );
	if (iter == m_map_hero_name.end())
		return NULL;

	return iter->second;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
t_hero const* t_adventure_map::get_mapped_hero( std::string const& name ) const
{
	t_hero_name_map::const_iterator iter = m_map_hero_name.find( name );
	if (iter == m_map_hero_name.end())
		return NULL;

	return iter->second;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
t_town* t_adventure_map::get_mapped_town( std::string const& name )
{
	t_town_name_map::iterator iter = m_map_town_name.find( name );
	if (iter == m_map_town_name.end())
		return NULL;

	return iter->second;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
t_town const* t_adventure_map::get_mapped_town( std::string const& name ) const
{
	t_town_name_map::const_iterator iter = m_map_town_name.find( name );
	if (iter == m_map_town_name.end())
		return NULL;

	return iter->second;
}

/*
** Set obelisk color as completed.
*/
void t_adventure_map::set_obelisk_completed( t_obelisk_color color, bool completed )
{
	assert( (color >= 0) && (color <= k_obelisk_color_count) );
	m_completed_obelisk_flags.set(color, completed);
}

/*
** Get obelisk color completed status.
*/
bool t_adventure_map::get_obelisk_completed( t_obelisk_color color) const
{
	assert( (color >= 0) && (color <= k_obelisk_color_count) );
	return m_completed_obelisk_flags.test(color);
}

/*
** Set obelisk color as placed.
*/
void t_adventure_map::set_obelisk_treasure_placed( t_obelisk_color color, bool placed )
{
	assert( (color >= 0) && (color <= k_obelisk_color_count) );
	m_obelisk_placed_treasure_flags.set(color, placed);
}

/*
** Get obelisk color placed status.
*/
bool t_adventure_map::get_obelisk_treasure_placed( t_obelisk_color color) const
{
	assert( (color >= 0) && (color <= k_obelisk_color_count) );
	return m_obelisk_placed_treasure_flags.test(color);
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::read_obelisk_data( std::streambuf & stream, int version ) 
{
	for (t_obelisk_color obelisk_color = t_obelisk_color(0); obelisk_color < k_obelisk_color_count; enum_incr(obelisk_color) )
	{
		if ( version >= 9 )
		{
			t_obelisk_marker_list &list = m_obelisk_marker_list[obelisk_color];

			int count = get< int >( stream );

			while ( count )
			{
				count--;

				t_obelisk_marker_list::iterator it = list.insert( list.end(), t_obelisk_marker() );

				if ( !it->read( stream ) )
					return false;
			}

		}

		m_obelisk_data[ obelisk_color ].read( obelisk_color, stream, version );
	}

	return true;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::write_obelisk_data( std::streambuf & stream ) const
{
	// Write out obelisk data
	for (t_obelisk_color obelisk_color = t_obelisk_color(0); obelisk_color < k_obelisk_color_count; enum_incr(obelisk_color) )
	{
		t_obelisk_marker_list const &list = m_obelisk_marker_list[obelisk_color];

		put< int >( stream, list.size() );

		t_obelisk_marker_list::const_iterator it;

		for (it = list.begin(); it != list.end(); it++)
		{
			if ( !it->write( stream ) )
				return false;
		}

		m_obelisk_data[ obelisk_color ].write( stream );
	}

	return true;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
t_shipyard_list& t_adventure_map::get_shipyards()
{
	return m_shipyards;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::add_shipyard( t_adv_shipyard& shipyard )
{
	t_shipyard_ptr ptr(&shipyard);
	assert(std::find( m_shipyards.begin(), m_shipyards.end(), ptr) == m_shipyards.end());
	m_shipyards.push_back( ptr );
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::remove_shipyard( t_adv_shipyard& shipyard )
{
	m_shipyards.remove( t_shipyard_ptr(&shipyard) );
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::read_allowed_artifacts( std::streambuf & stream, int version )
{
	if ( version < 13 )
	{
		for ( t_artifact_type type = t_artifact_type(0); type < k_artifact_type_count; enum_incr( type ) )
		{
			m_allowed_artifacts.set( type, !is_cut( type ) && allowed_by_default( type ) );
		}
	}
	else
	{
		if ( !m_allowed_artifacts.read( stream, function_2_handler( handle_allowed_artifacts) ) )
			return false;
	}

	return true;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::read_allowed_heroes( std::streambuf & stream, int version )
{
	if ( version < 13 )
	{
		for ( int portrait_id = 0; portrait_id < get_hero_portrait_count(); portrait_id++ )
		{
			m_allowed_heroes[ portrait_id ] = hero_allowed_by_default( portrait_id );
		}
	}
	else
	{
		if ( !m_allowed_heroes.read( stream ) )
			return false;

		if ( m_allowed_heroes.size() < get_hero_portrait_count() )
		{
			int old_size = m_allowed_heroes.size();
			m_allowed_heroes.resize( get_hero_portrait_count() );

			for ( int portrait_id = old_size; portrait_id < get_hero_portrait_count(); portrait_id++ )
			{
				m_allowed_heroes[ portrait_id ] = hero_allowed_by_default( portrait_id );
			}
		}
		else if ( m_allowed_heroes.size() > get_hero_portrait_count() )
		{
			return false;
		}
	}

	// Make sure m_default_heroes never returns a disallowed hero
	m_default_heroes.set_allowed_portrait_list( m_allowed_heroes );

	return true;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::read_allowed_skills( std::streambuf & stream, int version )
{
	if ( version < 13 )
	{
		m_allowed_skills.set();
	}
	else
	{
		if ( !m_allowed_skills.read( stream ) )
			return false;
	}

	return true;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::read_allowed_spells( std::streambuf & stream, int version )
{
	if ( version < 13 )
	{
		for ( t_spell spell = t_spell(0); spell < k_spell_count; enum_incr( spell ) )
		{
			m_allowed_spells.set( spell, is_teachable( spell ) );
		}
	}
	else
	{
		// Note: must handle addition of new spells correctly 
		// (default them to allowed)
		t_uint16 size = get< t_uint16 >( stream );

		if ( size > m_allowed_spells.size() )
			return false;

		t_uint16 bytes = ( size + 7 ) / 8;

		t_owned_array< unsigned char > buffer( new unsigned char[bytes] );

		if ( !::read( stream, buffer.get(), bytes ) )
			return false;

		int index;
		for ( index = 0; index < size; index++ )
		{
			m_allowed_spells.set( index, ( buffer[ index / 8 ] >> ( index % 8 ) ) & 0x1 );
		}

		// Default new spells
		for ( ; index < m_allowed_spells.size(); index++ )
		{
			m_allowed_spells.set( index, is_teachable( t_spell( index ) ) );
		}
	}

	return true;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::write_allowed_artifacts( std::streambuf & stream ) const
{
	return m_allowed_artifacts.write( stream );
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::write_allowed_heroes( std::streambuf & stream ) const
{
	return m_allowed_heroes.write( stream );
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::write_allowed_skills( std::streambuf & stream ) const
{
	return m_allowed_skills.write( stream );
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::write_allowed_spells( std::streambuf & stream ) const
{
	// Note: must handle addition of new spells correctly on read
	t_uint16 size = m_allowed_spells.size();
	put< t_uint16 >( stream, size );

	t_uint16 bytes = ( size + 7 ) / 8;

	t_owned_array< unsigned char > buffer( new unsigned char[bytes] );

	memset( buffer.get(), 0, bytes );

	int index;
	for ( index = 0; index < size; index++ )
	{
		if ( m_allowed_spells.test( index ) )
			buffer[ index / 8 ] |= ( 1 << ( index % 8 ) );
	}

	if ( !::write( stream, buffer.get(), bytes ) )
		return false;

	return true;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::read_carryover_artifacts( std::streambuf & stream, int version )
{
	if ( version >= 32 )
	{
		if (	!m_carry_in_artifacts.read( stream )
			||	!m_carry_out_artifacts.read( stream ) )
			return false;
	}
	else
	{
		m_carry_in_artifacts.reset();
		m_carry_out_artifacts.reset();
	}
	return true;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::write_carryover_artifacts( std::streambuf & stream ) const
{
	return m_carry_in_artifacts.write( stream ) && m_carry_out_artifacts.write( stream );
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::read_carryover_data( std::streambuf & stream, int version )
{
	if ( version >= 32 && get< t_int8 >( stream ) != 0 )
	{
		t_carryover_data_ptr new_carry_in_data( new t_carryover_data );
		if ( !new_carry_in_data->read( stream ) )
			return false;
		m_carry_in_data = new_carry_in_data;

		if ( version >= 38 )
		{
			t_carryover_data_ptr new_carry_out_data( new t_carryover_data );
			if ( !new_carry_out_data->read( stream ) )
				return false;
			m_carry_out_data = new_carry_out_data;
		}
		else
			m_carry_out_data = new t_carryover_data( *m_carry_in_data );
	}
	else
	{
		m_carry_in_data = 0;
		m_carry_out_data = 0;
	}

	return true;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::write_carryover_data( std::streambuf & stream ) const
{
	if ( m_carry_in_data.get() != 0 )
	{
		assert( m_carry_out_data.get() != 0 );

		put< t_int8 >( stream, true );
		if (	!m_carry_in_data->write( stream )
			||	!m_carry_out_data->write( stream ) )
			return false;
	}
	else
		put< t_int8 >( stream, false );

	return true;
}

// -------------------------------------------------------------------------------
// removes the team visibility for a given point and radius
// -------------------------------------------------------------------------------
void t_adventure_map::remove_visibility(
	int								team_num,
	t_level_map_point_2d const &	location,
	t_map_point_2d const &			size,
	int								radius,
	t_visibility_set &				changed_tile_points )
{
	if ( radius == 0 )
		return;

	t_map_point_2d center = t_map_point_2d( location ) * 2 + size;
	t_map_rect_2d visibility_rect = get_visibility_rect( center, radius, get_size() );

	// Build a visibility overlay at the object's old location
	t_player::t_visibility_overlay visibility_overlay( visibility_rect );

	// Iterate over all of the players on this team and mark the visibility for each
	int player_count = get_player_count();
	int player_num;
	for ( player_num = 0; player_num < player_count; ++player_num )
	{
		t_player const & player = get_player( player_num );
		if ( player.get_team() == team_num )
			player.mark_visibility( location.level, visibility_overlay );
	}

	// Iterate over the visibility overlay and mark as not visible any cells
	// which should no longer be visible
	t_level_map_point_2d tile_point( 0, 0, location.level );
	for (	tile_point.row = visibility_rect.top_left.row;
			tile_point.row < visibility_rect.bottom_right.row;
			++tile_point.row )
	{
		int first_col = get_row_start( tile_point.row );
		if ( first_col < visibility_rect.top_left.column )
			first_col = visibility_rect.top_left.column;

		int last_col = get_row_end( tile_point.row );
		if ( last_col > visibility_rect.bottom_right.column )
			last_col = visibility_rect.bottom_right.column;

		for (	tile_point.column = first_col;
				tile_point.column < last_col;
				++tile_point.column )
		{
			t_tile_visibility_data visibility_data = visibility_overlay[ tile_point ];

			t_tile & tile = get_tile( tile_point );
			if ( tile.get_visibility( team_num ) != k_tile_visible )
				continue;

			t_visibility_point new_point( tile_point, tile.get_visibility( team_num ), tile.get_anti_stealth( team_num ) );

			if ( !visibility_data.is_visible() )
			{
				tile.set_visibility( team_num, k_tile_not_visible );
				changed_tile_points.insert( new_point );
			}
			else
			{
				if ( tile.get_anti_stealth( team_num ) > visibility_data.get_anti_stealth() )
				{
					tile.set_anti_stealth( team_num, visibility_data.get_anti_stealth() );
					changed_tile_points.insert( new_point );
				}
			}
		}
	}
}

// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
namespace
{

	void do_increase_visibility(
		t_abstract_adventure_tile &			tile,
		t_map_point_2d const &				tile_point,
		int									team_num,
		t_tile_visibility					new_visibility,
		t_skill_mastery						new_scouting,
		t_increase_visibility_type			visibility_type,
		t_visibility_set &	changed_tile_points )
	{
		assert( new_visibility == k_tile_visible || new_scouting == k_mastery_none );

		if ( visibility_type == k_increase_visibility_land_only )
		{
			if ( is_water_terrain( tile.get_terrain() ) )
				return;
		}
		else if ( visibility_type == k_increase_visibility_water_only )
		{
			if ( !is_water_terrain( tile.get_terrain() ) )
				return;
		}

		t_tile_visibility tile_visibility = tile.get_visibility( team_num );
		t_visibility_point new_point( tile_point, tile_visibility, tile.get_anti_stealth( team_num ) );

		if ( new_visibility > tile_visibility )
		{
			tile.set_visibility( team_num, new_visibility, new_scouting );
			changed_tile_points.insert( new_point );
		}
		else if ( new_visibility == k_tile_visible && new_scouting > tile.get_anti_stealth( team_num ) )
		{
			tile.set_anti_stealth( team_num, new_scouting );
			changed_tile_points.insert( new_point );
		}
	}
}

// -------------------------------------------------------------------------------
// Set the visibiliy for an owned object
// -------------------------------------------------------------------------------
void t_adventure_map::increase_visibility(
	int								team_num,
	t_level_map_point_2d const &	location,
	t_map_point_2d const &			size,
	int								radius,
	t_tile_visibility				new_visibility,
	t_visibility_set &				changed_tile_points,
	t_increase_visibility_type		visibility_type )
{
	increase_visibility( team_num, location, size, radius, 0, new_visibility, k_mastery_none, changed_tile_points, visibility_type );
}

// -------------------------------------------------------------------------------
// Set the visibiliy for an owned object
// -------------------------------------------------------------------------------
void t_adventure_map::increase_visibility(
	int								team_num,
	t_level_map_point_2d const &	location,
	t_map_point_2d const &			size,
	int								scouting_range,
	int								attack_range,
	t_skill_mastery					new_scouting,
	t_visibility_set &				changed_tile_points )
{
	increase_visibility( team_num, location, size, scouting_range, attack_range, k_tile_visible, new_scouting, changed_tile_points );
}

// -------------------------------------------------------------------------------
// Set the visibiliy for an owned object
// -------------------------------------------------------------------------------
void t_adventure_map::increase_visibility(
	int								team_num,
	t_level_map_point_2d const &	location,
	t_map_point_2d const &			size,
	int								scouting_range,
	int								attack_range,
	t_tile_visibility				new_visibility,
	t_skill_mastery					new_scouting,
	t_visibility_set &				changed_tile_points,
	t_increase_visibility_type		visibility_type )
{
	assert( new_visibility >= 0 && new_visibility < k_tile_visibility_count );
	assert( new_scouting >= k_mastery_none && new_scouting < k_skill_mastery_count );
	assert( new_scouting == k_mastery_none || new_visibility == k_tile_visible );

	if ( scouting_range <= 0 )
		return;

	// Mark the cells in the scouting range
	int half_cell_radius_sq = scouting_range * scouting_range * 4;

	t_map_point_2d center = t_map_point_2d( location ) * 2 + size;
	t_map_rect_2d rect = get_visibility_rect( center, scouting_range, get_size() );

	t_level_map_point_2d tile_point( 0, 0, location.level );
	for (	tile_point.row = rect.top_left.row;
			tile_point.row < rect.bottom_right.row;
			++tile_point.row )
	{
		int first_col = get_row_start( tile_point.row );
		if ( first_col < rect.top_left.column )
			first_col = rect.top_left.column;

		int last_col = get_row_end( tile_point.row );
		if ( last_col > rect.bottom_right.column )
			last_col = rect.bottom_right.column;

		for (	tile_point.column = first_col;
				tile_point.column < last_col;
				++tile_point.column )
		{
			// Skip any cells outside of this object visibility radius
			int half_cell_delta_row = tile_point.row * 2 + 1 - center.row;
			int half_cell_delta_col = tile_point.column * 2 + 1 - center.column;
			int half_cell_distance_sq =
				half_cell_delta_row * half_cell_delta_row + half_cell_delta_col * half_cell_delta_col;
			if ( half_cell_distance_sq > half_cell_radius_sq )
				continue;

			do_increase_visibility(
				get_tile( tile_point ),
				tile_point,
				team_num,
				new_visibility,
				new_scouting,
				visibility_type,
				changed_tile_points );
		}
	}

	if ( attack_range <= 0 )
		return;

	// Bump the scouting value to mark the cells in the attack range
	if ( new_scouting < k_mastery_grand_master )
		enum_incr( new_scouting );

	rect = t_map_rect_2d( location, size );

	rect.top_left -= t_map_point_2d( attack_range, attack_range );
	if ( rect.top_left.row < 0 )
		rect.top_left.row = 0;
	if ( rect.top_left.column < 0 )
		rect.top_left.column = 0;

	rect.bottom_right += t_map_point_2d( attack_range, attack_range );
	int map_size = get_size();
	if ( rect.bottom_right.row > map_size )
		rect.bottom_right.row = map_size;
	if ( rect.bottom_right.column > map_size )
		rect.bottom_right.column = map_size;

	for (	tile_point.row = rect.top_left.row;
			tile_point.row < rect.bottom_right.row;
			++tile_point.row )
	{
		int first_col = get_row_start( tile_point.row );
		if ( first_col < rect.top_left.column )
			first_col = rect.top_left.column;

		int last_col = get_row_end( tile_point.row );
		if ( last_col > rect.bottom_right.column )
			last_col = rect.bottom_right.column;

		for (	tile_point.column = first_col;
				tile_point.column < last_col;
				++tile_point.column )
		{
			do_increase_visibility(
				get_tile( tile_point ),
				tile_point,
				team_num,
				new_visibility,
				new_scouting,
				visibility_type,
				changed_tile_points );
		}
	}
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::on_visibility_changed(
	int					team_num,
	int					level,
	t_visibility_set &	changed_tile_points )
{
	assert( team_num >= 0 && team_num < k_active_player_color_count );

	t_tile_point_set changed_shroud_set;
	build_visibility_difference_set( *this, team_num, level, changed_tile_points ).swap( changed_shroud_set );

	// If there's a shroud transition map for this team, recalculate the
	// transitions for the affected tiles
	if ( m_shroud_transition_maps[ team_num ].get() != 0 )
	{
		t_shroud_transition_calculator calculator;

		t_tile_point_set const & test_points 
			= build_transition_test_set( *this, changed_shroud_set );

		t_tile_point_set::const_iterator point_end = test_points.end();
		t_tile_point_set::const_iterator point_iter = test_points.begin();

		for ( ; point_iter != point_end; ++point_iter )
		{
			if ( calculator.set_transitions( *this, team_num, t_level_map_point_2d( *point_iter, level ) ) )
				changed_shroud_set.insert( *point_iter );
		}
	}

	t_map_renderer::on_visibility_changed(
		*this,
		team_num,
		level,
		changed_tile_points,
		changed_shroud_set );

	t_mini_map_renderer::on_visibility_changed(
		*this,
		team_num,
		level,
		changed_shroud_set );
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
t_shroud_transition_map * t_adventure_map::get_shroud_transition_map_ptr(
	int	team_num )
{
	assert( team_num >= 0 && team_num < k_active_player_color_count );
	return m_shroud_transition_maps[ team_num ].get();
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
t_shroud_transition_map const * t_adventure_map::get_shroud_transition_map_ptr(
	int	team_num ) const
{
	assert( team_num >= 0 && team_num < k_active_player_color_count );
	return m_shroud_transition_maps[ team_num ].get();
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::create_shroud_transition_maps()
{
	int team_num;
	for ( team_num = 0; team_num < k_active_player_color_count; ++team_num )
	{
		t_shroud_transition_map_ptr shroud_transition_map_ptr;
		if ( has_human_players( team_num ) )
			shroud_transition_map_ptr.reset( new t_shroud_transition_map( get_size(), get_num_levels() ) );
		m_shroud_transition_maps[ team_num ].swap( shroud_transition_map_ptr );
	}
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
void t_adventure_map::rebuild_shroud_transitions()
{
	create_shroud_transition_maps();

	int team_num;
	for ( team_num = 0; team_num < k_active_player_color_count; ++team_num )
	{
		if ( m_shroud_transition_maps[ team_num ].get() == 0 )
			continue;

		t_shroud_transition_calculator calculator;

		int size = get_size();
		int level_count = get_num_levels();

		int level;
		for ( level = 0; level < level_count; ++level )
		{
			t_map_point_2d tile_point;
			for ( tile_point.row = 0; tile_point.row < size; ++tile_point.row )
			{
				int row_end = get_row_end( tile_point.row );
				for (	tile_point.column = get_row_start( tile_point.row );
						tile_point.column < row_end;
						++tile_point.column )
					calculator.set_transitions( *this, team_num, t_level_map_point_2d( tile_point, level ) );
			}
		}
	}
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::write_shroud_transition_maps( std::streambuf & stream ) const
{
	// Write a bitset indicating the existence of shroud transtion maps for each team
	std::bitset< k_active_player_color_count > existence_flags;

	int team_num;
	for ( team_num = 0; team_num < k_active_player_color_count; ++team_num )
	{
		if ( m_shroud_transition_maps[ team_num ].get() != 0 )
			existence_flags[ team_num ] = true;
	}

	put_bitset( stream, existence_flags );

	// Write the transition maps
	for ( team_num = 0; team_num < k_active_player_color_count; ++team_num )
	{
		if ( m_shroud_transition_maps[ team_num ].get() == 0 )
			continue;

		t_shroud_transition_map const & transition_map = *m_shroud_transition_maps[ team_num ];

		int size = get_size();
		int level_count = get_num_levels();

		int level;
		for ( level = 0; level < level_count; ++level )
		{
			t_map_point_2d tile_point;
			for ( tile_point.row = 0; tile_point.row < size; ++tile_point.row )
			{
				int row_end = get_row_end( tile_point.row );
				for (	tile_point.column = get_row_start( tile_point.row );
						tile_point.column < row_end;
						++tile_point.column )
				{
					// Write the list of transtions for this tile
					typedef t_shroud_transition_map::t_transition_list t_transition_list;
					t_transition_list const & transitions =
						transition_map.get_transitions( t_level_map_point_2d( tile_point, level ) );

					put< t_uint8 >( stream, transitions.size() );

					t_transition_list::const_iterator transition_end = transitions.end();
					t_transition_list::const_iterator transition_iter = transitions.begin();
					for ( ; transition_iter != transition_end; ++transition_iter )
					{
						t_shroud_transition const & transition = *transition_iter;
						put< t_uint8 >( stream, transition.get_visibility() );
						put< t_uint16 >( stream, transition.get_id() );
						put< t_uint8 >( stream, transition.get_mask_set() );
					}
				}
			}
		}
	}

	return true;
}

// ---------------------------------------------------------------
// ---------------------------------------------------------------
bool t_adventure_map::read_shroud_transition_maps( std::streambuf & stream, int version )
{
	if ( version < 17 )
	{
		rebuild_shroud_transitions();
		return true;
	}

	std::bitset< k_active_player_color_count > existence_flags =
		get_bitset< k_active_player_color_count >( stream );

	int team_num;
	for ( team_num = 0; team_num < k_active_player_color_count; ++team_num )
	{
		if ( !existence_flags[ team_num ] )
			continue;

		int size = get_size();
		int level_count = get_num_levels();

		t_owned_ptr< t_shroud_transition_map > transition_map_ptr(
			new t_shroud_transition_map( size, level_count ) );

		int level;
		for ( level = 0; level < level_count; ++level )
		{
			t_map_point_2d tile_point;
			for ( tile_point.row = 0; tile_point.row < size; ++tile_point.row )
			{
				int row_end = get_row_end( tile_point.row );
				for (	tile_point.column = get_row_start( tile_point.row );
						tile_point.column < row_end;
						++tile_point.column )
				{
					typedef t_shroud_transition_map::t_transition_list t_transition_list;

					// Construct a new list of transitions for this tile
					t_transition_list new_transitions( get< t_uint8 >( stream ) );

					t_transition_list::iterator transition_end = new_transitions.end();
					t_transition_list::iterator transition_iter = new_transitions.begin();
					for ( ; transition_iter != transition_end; ++transition_iter )
					{
						t_shroud_transition & transition = *transition_iter;

						t_tile_visibility visibility = t_tile_visibility( get< t_uint8 >( stream ) );
						if ( visibility < 0 || visibility >= k_tile_visibility_count )
							return false;
						transition.set_visibility( visibility );
						transition.set_id( get< t_uint16 >( stream ) );
						transition.set_mask_set( get< t_uint8 >( stream ) );
					}

					transition_map_ptr->take_transitions(
						t_level_map_point_2d( tile_point, level ),
						new_transitions );
				}
			}
		}

		m_shroud_transition_maps[ team_num ].swap( transition_map_ptr );
	}

	return true;
}

// -------------------------------------------------------------------------------
// Read the terrain map
// -------------------------------------------------------------------------------
bool t_adventure_map::read_terrain_map( std::streambuf& buffer, int version )
{
	// read terrain
	t_adv_map_point		 point;
	int					 size;
	t_adventure_tile*	 tile;
	int                  tile_version = get_tile_version_from_saved_game_version( version );
	int                  map_levels = m_map.get_levels();
	int					 map_size   = m_map.get_size();

	for (point.level = 0; point.level < map_levels; point.level++)
	{
		for (point.row = 0; point.row < map_size; point.row++)
		{
			point.column = m_map.get_row_start( point.row );
			tile = &m_map.get( point );
			size = m_map.get_row_size( point.row );

			while (size--)
			{
				tile->read( buffer, tile_version );
				tile++;
			}
		}
	}

	return true;
}

// -------------------------------------------------------------------------------
// Read "month of the" data
// -------------------------------------------------------------------------------
bool t_adventure_map::read_monthly_creatures( std::streambuf & stream, int version )
{
	if ( version < 18 )
	{
		choose_next_month_creature();
	}
	else
	{
		m_previous_month_creature = t_creature_type( get< t_uint16 >( stream ) );
		m_next_month_creature = t_creature_type( get< t_uint16 >( stream ) );

		int size = get< t_uint16 >( stream );

		if ( size > 0 )
		{
			t_uint16 version = get< t_uint16 >( stream );

			while ( size )
			{
				m_respawn_point_list.push_back( t_respawn_point( stream, version ) );
				size--;
			}
		}
	}

	return true;
}

// -------------------------------------------------------------------------------
// Write "month of the" data
// -------------------------------------------------------------------------------
bool t_adventure_map::write_monthly_creatures( std::streambuf & stream ) const
{
	put< t_uint16 >( stream, m_previous_month_creature );
	put< t_uint16 >( stream, m_next_month_creature );

	put< t_uint16 >( stream, m_respawn_point_list.size() );

	if ( !m_respawn_point_list.empty() )
	{
		put< t_uint16 >( stream, t_respawn_point::get_version() );

		t_respawn_point_list::const_iterator it;

		for ( it = m_respawn_point_list.begin(); it != m_respawn_point_list.end(); it++ )
		{
			if ( !it->write( stream ) )
				return false;
		}
	}
	return true;
}

//////////////////////////////////////////////////
// event	functions					

// -------------------------------------------------------------------------------
// record_replay_shroud : records the shroud map for the local player at the end of their turn.
// the shroud is difficult to rollback piecemeal through the event capture mechanism, so we just
// do a brute force copy of it at the end of the players turn.
// -------------------------------------------------------------------------------
bool		 t_adventure_map::record_replay_shroud()
{
	m_replay_terrain_map.clear_buffer();

	{
		//deflate stream witihin its own block to force its destructor call upon 
		//leaving the block (finishes write on buffer)

		t_deflate_filter		deflate_buffer( m_replay_terrain_map );

		write_terrain_map(deflate_buffer);

		deflate_buffer.pubsync();
	}

	m_replay_terrain_map.size();

	/////////////////////////////////////////////////////////////////////////////////

	m_replay_shroud_transition_map.clear_buffer();

	{
		//deflate stream witihin its own block to force its destructor call upon 
		//leaving the block (finishes write on buffer)

		t_deflate_filter		deflate_buffer( m_replay_shroud_transition_map );

		write_shroud_transition_maps(deflate_buffer);

		deflate_buffer.pubsync();
	}

	m_replay_shroud_transition_map.size();

	m_replay_terrain_map.compact();
	m_replay_shroud_transition_map.compact();

	return true;
}

// -------------------------------------------------------------------------------
// use_replay_shroud : use the replay shroud record when record_replay_shroud was called.
// -------------------------------------------------------------------------------
bool		 t_adventure_map::use_replay_shroud()
{
	if (m_replay_terrain_map.size() == 0 || m_replay_shroud_transition_map.size() == 0) 
		return false;

	m_replay_terrain_map.reset_for_read();

	t_inflate_filter					terrain_inflate_buffer( m_replay_terrain_map );
	
	read_terrain_map( terrain_inflate_buffer , m_event_header.version );

	/////////////////////////////////////////////////////////////////////////////////

	m_replay_shroud_transition_map.reset_for_read();
	
	t_inflate_filter					shroud_inflate_buffer( m_replay_shroud_transition_map );
	
	read_shroud_transition_maps( shroud_inflate_buffer , m_event_header.version );

	return true;
}

// -------------------------------------------------------------------------------
// create_replay_map : a replay map is a clone of the current map using the seralize -> reload 
// technqiue. The replay map also contains a copy of the event buffer which is used to replay
// the events. 
// -------------------------------------------------------------------------------
t_adventure_map_ptr			t_adventure_map::create_replay_map( t_progress_handler*	handler )
{
	//clones the adventure map from a saved in memory buffer of the current(this) map.

	t_memory_buffer			save_buffer;
	
	{
		//deflate stream witihin its own block to force its destructor call upon 
		//leaving the block (finishes write on buffer)

		t_deflate_filter		deflate_buffer( save_buffer );

		this->write(deflate_buffer, handler);

		deflate_buffer.pubsync();
	}

	handler->increment(1);

	save_buffer.pubsync();
	save_buffer.reset_for_read();

	t_saved_game_header		header;
	t_inflate_filter		inflate_buffer_header( save_buffer );

	if (!::read( inflate_buffer_header, header, handler))
		return NULL;

	//need the save header to properly reconstruct the adv object from the raw data.
	m_event_header = header;

	save_buffer.reset_for_read();

	t_inflate_filter		inflate_buffer_game( save_buffer );
	t_adventure_map_ptr		replay_map_ptr( new t_adventure_map( header.size, header.levels, header.map_random_seed,
                                header.player_difficulty, header.turn_duration_index,
								m_guards_can_move ));

	//the replay map doesnt reset the random number generator....
	//random = t_random_number_generator( header.random_seed );

	if (!replay_map_ptr->read( inflate_buffer_game, handler ))
		return NULL;
	
	//replay map should not be recording events.

	replay_map_ptr->set_event_header(m_event_header);
	replay_map_ptr->stop_event_record();
	
	replay_map_ptr->rollback_replay_events(m_player_number);
	replay_map_ptr->use_replay_shroud();
	replay_map_ptr->set_is_replay_map(true);

	//need to call update state to force an update of the armies state if 
	//they are in a graveyard currently, otherwise they dont get reset because the 
	//replay map rarely calls update state upon armies.

	t_army_list::iterator army_iter;

	for (army_iter = m_armies.begin(); army_iter != m_armies.end(); army_iter++)
	{
		army_iter->get()->update_state();
	}

	return replay_map_ptr;
}

// -------------------------------------------------------------------------------
// cache_adventure_object_state : adds the objects state into the state cache
// -------------------------------------------------------------------------------
void		t_adventure_map::cache_adventure_object_state ( t_adventure_object * object )
{
	m_replay_event_state_map->insert(object);
}

// -------------------------------------------------------------------------------
// attach_event_to_state_cache : only 3 things refernce the state cache, towns, armies and events.
// -------------------------------------------------------------------------------
void		t_adventure_map::attach_event_to_state_cache( t_adventure_object * object, t_adventure_event_base * event )
{
	t_adventure_global_id						global_id = object->get_global_id();
	
	if (m_replay_event_state_map->is_empty(global_id))
		return;

	event->set_adventure_manager_for_memory_cache( this );
	event->set_cache_for_memory_cache( m_replay_event_state_map->get_memory_cache(object) );
	event->set_global_id_for_memory_cache( global_id );
}

// -------------------------------------------------------------------------------
// attach_event_to_adventure_map : attaches the event to the adventure map
// -------------------------------------------------------------------------------
void		t_adventure_map::attach_event_to_adventure_map ( t_adventure_object * object, t_adventure_event_base * new_event )
{
	if (!new_event->is_valid()) 
		return;
	
	new_event->set_handler( bound_handler( *this, &t_adventure_map::adventure_event_finished ) );
	new_event->set_players_turn_on(m_current_player_number);
	new_event->set_event_tick(m_event_tick);

	m_event_buffer.push_back(new_event);
	
	attach_event_to_state_cache( object, new_event );
}

// -------------------------------------------------------------------------------
// memory_cache_refrence_remove : removes the object cache from the buffer becuase there
//	are no more refrences to it etiher within the event buffer or the army/town itself.
// -------------------------------------------------------------------------------
void		t_adventure_map::memory_cache_refrence_remove ( t_adventure_global_id	object_gid )
{
	m_replay_event_state_map->remove( object_gid );
}

// -------------------------------------------------------------------------------
// get_cache_manager : gets cache manager for this map.
// -------------------------------------------------------------------------------
t_adventure_object_cache_manager * t_adventure_map::get_cache_manager()
{
	return m_replay_event_state_map;
}

// -------------------------------------------------------------------------------
// record_mover_event : record mover events generated by army movers. these mover events
// require a path. 
// -------------------------------------------------------------------------------
bool		 t_adventure_map::record_mover_event( t_army* object,  t_adventure_path const & path , bool limit_move, t_adv_map_point orig_point )
{
	if (object->is_event_recordable() == false || m_record_replay_events == false)
		return false;

	t_adventure_event_ptr	 new_event(new t_adventure_event_mover(object,path,limit_move,orig_point));

	attach_event_to_adventure_map( object, new_event );

	return true;
}

// -------------------------------------------------------------------------------
// record_look_trigger_event : when an actor looks at a trigger to activate it 
// this event is recorded.
// -------------------------------------------------------------------------------
bool		 t_adventure_map::record_look_trigger_event( t_actor* object , t_direction dir )
{
	if (object->is_event_recordable() == false || m_record_replay_events == false)
		return false;
	
	t_adventure_event_ptr	 new_event(new t_adventure_event_look_trigger(object,dir));
	
	attach_event_to_adventure_map( object, new_event );
	
	return true;
}

// -------------------------------------------------------------------------------
// record_placement_event : place event is actually a creational event, when an object 
// is loaded from a map , save game or created wtihin the game the place_object function is 
// called as well as record_placement_event
// -------------------------------------------------------------------------------
bool		 t_adventure_map::record_placement_event(  t_adventure_object* object , t_level_map_point_2d point )
{
	if (object->is_event_recordable() == false || m_record_replay_events == false)
		return false;

	t_adventure_event_ptr	 new_event(new t_adventure_event_place(object));

	cache_adventure_object_state( object );

	attach_event_to_adventure_map( object, new_event );

	return true;
}

// -------------------------------------------------------------------------------
// record_remove_event : remove events are generated on adv_objects which enter towns, lose battles, 
// etc.. and removed from the adventure map.
// -------------------------------------------------------------------------------
bool		 t_adventure_map::record_remove_event(  t_adventure_object* object )
{
	if (object->is_event_recordable() == false || m_record_replay_events == false)
		return false;

	t_adventure_global_id						global_id	= object->get_global_id();
	t_level_map_point_2d						point		= object->get_position();

	//need valid guid
	if (global_id == k_invalid_global_id) 
		return false;
	
	t_adventure_event_ptr	 new_event;

	//doesnt have to be in the state cache but it would be more accurate
	//to use the previous state of the object instead of its current state.
	if (m_replay_event_state_map->is_empty(global_id))
	{
		new_event = new t_adventure_event_remove( object, point );
	}
	else
	{
		t_adventure_object_memory_cache *	cache = m_replay_event_state_map->get_memory_cache(object);
		
		t_memory_buffer_counted_ptr	const &		pre_update_mem_buffer = cache->get_data_buffer();
		
		//must have valid state buffer
		if ( pre_update_mem_buffer == NULL )
			return false;

		new_event = new t_adventure_event_remove( object, point,pre_update_mem_buffer );
	}

	attach_event_to_adventure_map( object, new_event );

	return true;
}

// -------------------------------------------------------------------------------
// record_set_action_event : set the action state of the entity
// -------------------------------------------------------------------------------
bool		t_adventure_map::record_set_action_event( t_actor* object,  t_adv_actor_action_id action_state )
{
	if (object->is_event_recordable() == false || m_record_replay_events == false)
		return false;
		
	//the only action we care about is the attack action, all others are culled out
	//add more action states if needed.

	switch(action_state)
	{
	case k_adv_actor_action_attack:
	
		break;
	default:
		return false;
		break;
	}

	t_adventure_event_ptr	 new_event(new t_adventure_event_set_action(object,action_state));
		
	attach_event_to_adventure_map( object, new_event );

	return true;
}

// -------------------------------------------------------------------------------
// record_set_owner_event : record change in owner ship for mines/towns etc...
// -------------------------------------------------------------------------------
bool		 t_adventure_map::record_set_owner_event( t_owned_adv_object* object, int owner )
{
	if (object->is_event_recordable() == false || m_record_replay_events == false)
		return false;
	
	t_adventure_event_ptr	 new_event(new t_adventure_event_set_owner(object,owner));

	attach_event_to_adventure_map( object, new_event );

	return true;
}

// -------------------------------------------------------------------------------
// record_set_owner_event : record change in owner ship of boats/armies etc..
// -------------------------------------------------------------------------------
bool		 t_adventure_map::record_set_owner_event( t_army * object, int owner )
{
	if (object->is_event_recordable() == false || m_record_replay_events == false)
		return false;
	
	t_adventure_event_ptr	 new_event(new t_adventure_event_set_owner(object,owner));

	attach_event_to_adventure_map( object, new_event );

	return true;
}

// -------------------------------------------------------------------------------
// record_teleport_event : records teleport events from portals and tunnels.
// -------------------------------------------------------------------------------
bool		 t_adventure_map::record_teleport_event ( t_adventure_object* object, t_adv_map_point point_a, 
													 t_level_map_point_2d point_b )
{	
	if (object->is_event_recordable() == false || m_record_replay_events == false)
		return false;
	
	t_adventure_event_ptr	 new_event(new t_adventure_event_teleport(object,point_a,point_b));

	attach_event_to_adventure_map( object, new_event );

	return true;
}

// -------------------------------------------------------------------------------
// record_update_event : an update event records a visible state change of an adventure object
// -------------------------------------------------------------------------------
bool		 t_adventure_map::record_update_event ( t_adventure_object* object )
{
	if (object->is_event_recordable() == false || m_record_replay_events == false)
		return false;
	
	t_adventure_global_id						global_id = object->get_global_id();
	t_level_map_point_2d						point = object->get_position();

	//need valid guid
	if (global_id == k_invalid_global_id) 
		return false;

	//need to be in the state cache map
	if (m_replay_event_state_map->is_empty(global_id))
		return false;
	
	t_adventure_object_memory_cache *	cache = m_replay_event_state_map->get_memory_cache(object);

	t_memory_buffer_counted_ptr	const &		pre_update_mem_buffer = cache->get_data_buffer();

	//must have valid state buffer
	if ( pre_update_mem_buffer == NULL )
		return false;

	t_adventure_event_ptr	 new_event(new t_adventure_event_update( object , point , pre_update_mem_buffer ));
	
	//dont forget to update the state map...
	m_replay_event_state_map->update(object);

	attach_event_to_adventure_map( object, new_event );
	
	return true;
}

// -------------------------------------------------------------------------------
// record_visiblity_event : eyeball triggers and stuff generate this event, which modify
//		the shroud in some way.
// -------------------------------------------------------------------------------
bool		 t_adventure_map::record_visiblity_event ( t_army* object, t_adv_map_point const& point, 
		                          t_direction direction, t_adventure_object* source )
{
	if (object->is_event_recordable() == false || m_record_replay_events == false)
		return false;
	
	t_adventure_event_ptr	 new_event(new t_adventure_event_visiblity( object , point , direction,source ));

	attach_event_to_adventure_map( object, new_event );

	return true;
}

// -------------------------------------------------------------------------------
// record_adv_spell_effect : adventure spells are special in that they are usually modal and their event
//	cannot be captured through the place event correctly.
// -------------------------------------------------------------------------------
bool		t_adventure_map::record_adv_spell_effect( t_army* target, k_spell_effects_event_id spell_id,
													 bool run_modal )
{
	if (target->is_event_recordable() == false || m_record_replay_events == false)
		return false;

	t_adventure_event_ptr	 new_event(new t_adventure_event_spells_effect( target->get_global_id() , spell_id , run_modal ));
	
	attach_event_to_adventure_map( target, new_event );
	
	return true;
}

// -------------------------------------------------------------------------------
// adventure_event_finished : events when they finsih use this callback to tell the 
//	playback object to move onto the next event.
// -------------------------------------------------------------------------------
void		t_adventure_map::adventure_event_finished( t_adventure_event_base * event)
{
	m_event_playback.adventure_event_finished(event);

	return;
}

//////////////////////////////////////////////////


// -------------------------------------------------------------------------------
// Read win/loss strings
// -------------------------------------------------------------------------------
bool t_adventure_map::read_winloss_strings( std::streambuf & stream, int version )
{
	if ( version < 16 )
	{
		for (t_player_color color = t_player_color(0); color < k_player_gray; enum_incr( color ))
		{
			m_victory_condition_string[color] = k_default_victory_condition;
			m_loss_condition_string[color] = k_default_loss_condition;
		}
	}
	else
	{
		if ( version < 46 )
		{
			std::string loss, victory;
			if (    !read_string16( stream, loss) 
				 || !read_string16( stream, victory) )
			{
				return false;
			}

			for (t_player_color color = t_player_color(0); color < k_player_gray; enum_incr( color ))
			{
				m_loss_condition_string[ color ] = loss;
				m_victory_condition_string[ color ] = victory;
			}
		}
		else
		{
			for (t_player_color color = t_player_color(0); color < k_player_gray; enum_incr( color ))
			{
				if (!read_string16( stream, m_loss_condition_string[ color ]))
					return false;
				if (!read_string16( stream, m_victory_condition_string[ color ]))
					return false;
			}
		}
	} 
	
	return true;
}

// -------------------------------------------------------------------------------
// clear the event buffer
// -------------------------------------------------------------------------------
t_saved_game_header const & t_adventure_map::get_event_header( )
{
	return m_event_header;
}

// -------------------------------------------------------------------------------
// clear the event buffer
// -------------------------------------------------------------------------------
void				t_adventure_map::set_event_header( t_saved_game_header const & event_header )
{
	m_event_header = event_header;
}

// -------------------------------------------------------------------------------
// clear the event buffer
// -------------------------------------------------------------------------------
void				 t_adventure_map::update_event_buffer( int player_id )
{
	t_adventure_event_list::iterator		event_iter								= m_event_buffer.begin();
	t_adventure_event_list::iterator		remove_event_iter_end					= m_event_buffer.end();

	int size = m_event_buffer.size();

	//early exit 
	if (event_iter != m_event_buffer.end())
	{
		if ( event_iter->get()->get_event_tick()  == m_event_tick )
			return;
	}

	//find the first occurance of the current event tick, cull out the rest
	while (event_iter != m_event_buffer.end())
	{
		if ( event_iter->get()->get_event_tick()  == m_event_tick )
		{
			remove_event_iter_end = event_iter;
			break;
		}
		
		event_iter++;
	}
	
	m_event_buffer.erase( m_event_buffer.begin(),remove_event_iter_end );

	size = m_event_buffer.size();
}

// -------------------------------------------------------------------------------
// start the event recording process
// -------------------------------------------------------------------------------
void				 t_adventure_map::start_event_record()
{
	m_record_replay_events = true;
}

// -------------------------------------------------------------------------------
// stop the event recording process
// -------------------------------------------------------------------------------
void				 t_adventure_map::stop_event_record()
{
	m_record_replay_events = false;
}

// -------------------------------------------------------------------------------
// start the event playback process
// -------------------------------------------------------------------------------
void				 t_adventure_map::start_event_playback()
{
	m_event_playback.start();
}

// -------------------------------------------------------------------------------
// stop the event playback process
// -------------------------------------------------------------------------------
void				 t_adventure_map::stop_event_playback()
{
	m_event_playback.stop();
}

// -------------------------------------------------------------------------------
// stop the event playback process
// -------------------------------------------------------------------------------
void				t_adventure_map::event_playback_finish()
{
	//do any clean up if we need to here.
	m_event_playback.stop();
	m_map_window->get_frame()->replay_turn_finish();
}

// -------------------------------------------------------------------------------
// the event tick is used to keep track of the events which happen after the 
// local players turn, so we can cull out correctly their events and all previous
// old events on their turn.
// -------------------------------------------------------------------------------
void				t_adventure_map::increment_event_tick()
{
	m_event_tick++;
}

// -------------------------------------------------------------------------------
// rollbacks rolls the map back by calling the undo_event back to the 
// end of the select_team turn.
// -------------------------------------------------------------------------------
void				 t_adventure_map::rollback_replay_events(int select_team)
{
	t_adventure_event_list::const_iterator event_iter = m_event_buffer.end();

	int size = m_event_buffer.size();

	while(size > 0)
	{
		event_iter--;
		size--;
		
		event_iter->get()->undo_event( this, m_event_header );
	}
}

// -------------------------------------------------------------------------------
// Write the terrain map
// -------------------------------------------------------------------------------
bool t_adventure_map::write_terrain_map( std::streambuf& buffer ) const
{
	// write out terrain
	t_adv_map_point					point;
	int								size;
	t_adventure_tile const *		tile;
	int								map_levels  = m_map.get_levels();
	int								map_size	= m_map.get_size();

	for (point.level = 0; point.level < map_levels; point.level++)
	{
		for (point.row = 0; point.row < map_size; point.row++)
		{
			point.column = m_map.get_row_start( point.row );
			tile = &m_map.get( point );
			size = m_map.get_row_size( point.row );

			while (size--)
			{
				tile->write( buffer );
				tile++;
			}
		}
	}

	return true;
}

// -------------------------------------------------------------------------------
// Write win/loss strings
// -------------------------------------------------------------------------------
bool t_adventure_map::write_winloss_strings( std::streambuf & stream ) const
{
	for (t_player_color color = t_player_color(0); color < k_player_gray; enum_incr( color ))
	{
		if (!write_string16( stream, m_loss_condition_string[ color ]))
			return false;
		if (!write_string16( stream, m_victory_condition_string[ color ]))
			return false;
	}
	return true;
}

// -------------------------------------------------------------------------------
// Add caravan to caravan map
// -------------------------------------------------------------------------------
void t_adventure_map::add_caravan( t_caravan_ptr caravan )
{
	m_caravans.insert( caravan );
}

// -------------------------------------------------------------------------------
// Remove caravan from caravan map
// -------------------------------------------------------------------------------
void t_adventure_map::remove_caravan( t_caravan_ptr caravan )
{
	// Warning! since m_caravans is a multiset, calling 
	// m_caravans.erase( caravan ) would be BAD
	t_caravan_set::iterator index;

	index = m_caravans.find( caravan );
	while ( index != m_caravans.end() && *index != caravan )
		index++;

	if (index == m_caravans.end())
	{
		assert( false );
	}
	else
	{
		m_caravans.erase( index );
	}
}




// -------------------------------------------------------------------------------
// Read caravans in from save file
// -------------------------------------------------------------------------------
bool t_adventure_map::read_caravans( std::streambuf & buffer, int version )
{
	if ( version < 20 )
		return true;

	t_uint32 count = get< t_uint32 >( buffer );

	if ( count > 0 )
	{
		t_uint16 caravan_version = get< t_uint16 >( buffer );

		while ( count > 0 )
		{
			t_caravan_ptr caravan = new t_caravan( this );

			if ( !caravan->read( buffer, caravan_version ) )
				return false;

			m_caravans.insert( caravan );

			get_player( caravan->get_owner_number() ).add( caravan );

			caravan->read_postplacement( *this );

			count--;
		}
	}

	return true;
}

// -------------------------------------------------------------------------------
// Write caravans out to save file
// -------------------------------------------------------------------------------
bool t_adventure_map::write_caravans( std::streambuf & buffer ) const
{
	put< t_uint32 >( buffer, m_caravans.size() );

	if ( !m_caravans.empty() )
	{
		put< t_uint16 >( buffer, t_caravan::get_caravan_version() );

		t_caravan_set::const_iterator it;

		for ( it = m_caravans.begin(); it != m_caravans.end(); it++ )
		{
			if ( !(*it)->write( buffer ) )
				return false;
		}
	}

	return true;
}

// -------------------------------------------------------------------------------
// Determine the importance of the given artifact
// -------------------------------------------------------------------------------
t_ai_importance t_adventure_map::get_ai_importance_for_artifact( t_artifact const & artifact ) const
{
	t_artifact_type type = artifact.get_icon();

	// Look for spells
	t_spell spell = k_spell_none;

	t_artifact_effect_list const & effects = artifact.get_effects();
	
	t_artifact_effect_list::const_iterator it;

	for ( it = effects.begin(); spell == k_spell_none && it != effects.end(); it++ )
	{
		t_artifact_effect_ptr effect = *it;
		t_artifact_prop::t_single_spell const * spell_effect = dynamic_cast< t_artifact_prop::t_single_spell const * >( effect.get() );

		if ( spell_effect ) 
		{
			spell = spell_effect->get_spell();
		}
	}

	return get_ai_importance_for_artifact( type, spell );
}

// -------------------------------------------------------------------------------
// Determine the importance of the given type of artifact
// -------------------------------------------------------------------------------
t_ai_importance t_adventure_map::get_ai_importance_for_artifact( t_artifact_type type, t_spell spell ) const
{
	if ( type == k_artifact_parchment || type == k_artifact_scroll )
	{
		t_spell_ai_importance_map const & appropriate_map = ( type == k_artifact_parchment ? m_parchment_ai_importance_map : m_scroll_ai_importance_map );

		t_spell_ai_importance_map::const_iterator it = appropriate_map.find( spell );
		
		// Normal importance artifacts are not included on importance list
		if ( it == appropriate_map.end() )
			return k_ai_importance_normal;
		else
			return it->second;
	}
	else
	{
		t_artifact_ai_importance_map::const_iterator it = m_artifact_ai_importance_map.find( type );
		
		// Normal importance artifacts are not included on importance list
		if ( it == m_artifact_ai_importance_map.end() )
			return k_ai_importance_normal;
		else
			return it->second;
	}
}

// -------------------------------------------------------------------------------
// Read the artifact ai importance information from a save game
// -------------------------------------------------------------------------------
bool t_adventure_map::read_ai_importance_maps( std::streambuf & stream, int version )
{
	if ( version < 28 )
	{
		m_artifact_ai_importance_map.clear();
		m_parchment_ai_importance_map.clear();
		m_scroll_ai_importance_map.clear();

		return true;
	}
	else
	{
		return (    m_artifact_ai_importance_map.read( stream ) 
				 && m_parchment_ai_importance_map.read( stream )
				 && m_scroll_ai_importance_map.read( stream ) );
	}
}

// -------------------------------------------------------------------------------
// Read the artifact ai importance information from a map file
// -------------------------------------------------------------------------------
bool t_adventure_map::read_ai_importance_maps_from_map( std::streambuf & stream, t_map_header const& header )
{
	if ( header.version < 23 )
	{
		m_artifact_ai_importance_map.clear();
		m_parchment_ai_importance_map.clear();
		m_scroll_ai_importance_map.clear();

		return true;
	}
	else
	{
		return (    m_artifact_ai_importance_map.read( stream ) 
				 && m_parchment_ai_importance_map.read( stream )
				 && m_scroll_ai_importance_map.read( stream ) );
	}
}

// -------------------------------------------------------------------------------
// Write the artifact ai importance information to a save game
// -------------------------------------------------------------------------------
bool t_adventure_map::write_ai_importance_maps( std::streambuf & stream ) const
{
	return (    m_artifact_ai_importance_map.write( stream ) 
			 && m_parchment_ai_importance_map.write( stream )
			 && m_scroll_ai_importance_map.write( stream ) );
}


// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
bool t_adventure_map::read_cut_scene_info( std::streambuf & stream, int version )
{
	if ( version >= 29 )
	{
		if ( !m_prologue_info.read( stream ) || !m_epilogue_info.read( stream ) )
			return false;
	}
	else
	{
		m_prologue_info = t_scenario_cut_scene_info();
		m_epilogue_info = t_scenario_cut_scene_info();
	}
	return true;
}

// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
bool t_adventure_map::write_cut_scene_info( std::streambuf & stream ) const
{
	return m_prologue_info.write( stream ) && m_epilogue_info.write( stream );
}

// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
t_carryover_data_ptr t_adventure_map::build_carry_out_data()
{
	typedef t_player::t_creature_stack_set t_creature_stack_set;

	t_carryover_data_ptr result = m_carry_out_data;
	if ( result.get() == 0 )
		result = new t_carryover_data;
	else
		m_carry_out_data = 0;

	int human_player_num;

	// First find the human player
	for ( human_player_num = 0; true; ++human_player_num )
	{
		assert( human_player_num < get_player_count() );
		if ( !get_player( human_player_num ).is_computer() )
			break;
	}

	// Add the data from the human player to the carryover data
	{
		t_creature_stack_set const & stacks = build_creature_stack_set( *this, human_player_num );
		t_creature_stack_set::const_iterator stack_end = stacks.end();
		t_creature_stack_set::const_iterator stack_iter = stacks.begin();
		for ( ; stack_iter != stack_end; ++stack_iter )
		{
			t_creature_stack const & stack = **stack_iter;
			if ( stack.get_hero() != 0 )
			{
				t_hero const & hero = *stack.get_hero();
				t_hero_carryover_data_ptr new_hero_data_ptr( new t_hero_carryover_data );
				hero.store( *new_hero_data_ptr );
				strip_extra_artifacts( *new_hero_data_ptr, m_carry_out_artifacts );
				result->add_for_human( new_hero_data_ptr );
			}
			else
			{
				assert( stack.get_creature() != 0 );
				t_creature const & creature = *stack.get_creature();
				add_artifacts_to_carryover_data( creature, m_carry_out_artifacts, *result );
			}
		}
	}

	// Add the data from all of the non-human players
	int player_count = get_player_count();
	int player_num;
	for ( player_num = 0; player_num < player_count; ++player_num )
	{
		if ( player_num == human_player_num )
			continue;

		t_player const & player = get_player( player_num );
		assert( player.is_computer() );

		t_creature_stack_set const & stacks = build_creature_stack_set( *this, player_num );
		t_creature_stack_set::const_iterator stack_end = stacks.end();
		t_creature_stack_set::const_iterator stack_iter = stacks.begin();
		for ( ; stack_iter != stack_end; ++stack_iter )
		{
			t_creature_stack const & stack = **stack_iter;
			if ( stack.get_hero() != 0 )
			{
				t_hero const & hero = *stack.get_hero();
				t_hero_carryover_data_ptr new_hero_data_ptr( new t_hero_carryover_data );
				hero.store( *new_hero_data_ptr );
				strip_extra_artifacts( *new_hero_data_ptr, t_artifact_set() );
				result->add( new_hero_data_ptr, player.get_color() );
			}
		}
	}

	return result;
}

// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
bool t_adventure_map::has_empty_boat( t_player const& player )
{
	t_army_list::const_iterator it;
	t_army_list::const_iterator it_end = m_armies.end();
	for ( it = m_armies.begin(); it != it_end; it++ )
	{
		t_army_ptr army = *it;
		if (army->is_boat() && army->empty() && (army->get_owner() == &player))
			return true;
	} 
	return false;
}

// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
t_carryover_data * t_adventure_map::get_carry_in_data()
{
	return m_carry_in_data.get();
}

// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
t_carryover_data const * t_adventure_map::get_carry_out_data() const
{
	return m_carry_out_data.get();
}

// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
t_carryover_data * t_adventure_map::get_carry_out_data()
{
	return m_carry_out_data.get();
}

// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
bool t_adventure_map::is_game_over()
{
	return m_game_over || m_map_window == 0 || !m_map_window->get_frame()->is_open();
}

// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
int t_adventure_map::get_team_lighthouse_count( int player_number ) const
{
	if (player_number < 0)
		return 0;

	int i;
	int result = 0;
	t_player const*	player = m_players[player_number].get();
	int				team = player->get_team();

	for (i = 0; i < m_players.size(); ++i)
		if (m_players[i]->get_team() == team)
			result += m_players[i]->get_lighthouse_count();
	return result;
}

// -------------------------------------------------------------------------------
// start a neutral turn
// -------------------------------------------------------------------------------
void t_adventure_map::begin_neutral_turn()
{
	t_object_id object_id;

	for (	object_id = get_first_object_id();
			object_id != k_invalid_object_id;
			object_id = get_next_object_id( object_id ) )
	{
		t_adventure_object & object = get_adv_object( object_id );

		if ( object.get_owner() == 0 )
			object.on_begin_turn();
	}
}

// --------------------------------------------------------
// adventure map
// --------------------------------------------------------
int t_adventure_map::get_next_player() const
{
	int next_player_number = m_current_player_number;
	
	while (true)
	{
		next_player_number = (next_player_number + 1) % m_players.size();

		if ( !m_players[next_player_number]->is_eliminated() )
		{
			// We found a living player to give the turn to
			break;
		}

		if (next_player_number == m_current_player_number)
		{
			// Break out of endless cycle -- everybody's dead!?
			break;
		}
	}
	return next_player_number;
}

// --------------------------------------------------------
// do all things that happen at the end of the day
// --------------------------------------------------------
void t_adventure_map::process_day_end( t_adventure_frame* frame )
{
	m_in_neutral_turn = true;
	get_ai().do_neutral_player();

	if (is_game_over() )
		return;

	t_object_id object_id;

	for (	object_id = get_first_object_id();
			object_id != t_adventure_map::k_invalid_object_id;
			object_id = get_next_object_id( object_id ) )
	{
		t_adventure_object & object = get_adv_object( object_id );

		if ( object.get_owner() == 0 )
			object.on_end_turn();
	}
	m_in_neutral_turn = false;

	set_current_player( get_next_player() );

	process_new_day();
	frame->set_day( get_day() );
	if (get_winning_team() != -1)
		decrement_victory_timer();
}

// --------------------------------------------------------
// select the next player and possibly process new day
// --------------------------------------------------------
void t_adventure_map::set_next_player( t_adventure_frame* frame )
{
	int old_player_number = m_current_player_number;
	int next_player_number = get_next_player();
	
	if (next_player_number <= old_player_number)
	{
		begin_neutral_turn();
		process_day_end( frame );
	}
	else
	{
		set_current_player( next_player_number );
	}
}

// --------------------------------------------------------
// resume a turn from a saved game.
// --------------------------------------------------------
void t_adventure_map::resume_turn( t_adventure_frame* frame )
{
	if (m_in_neutral_turn)
		process_day_end( frame );

	if (is_game_over())
		return;

	t_player& player = get_current_player();

	if (player.is_computer())
	{
		get_ai().do_turn( frame );
		if (is_game_over())
			return;
		frame->do_end_turn();
	}
}

// --------------------------------------------------------
// select the least-used spells from a set
// --------------------------------------------------------
t_spell_set t_adventure_map::select_spells( t_spell_set const& spells ) const
{
	t_spell		spell;
	t_spell_set result;
	int			lowest;

	if (spells.none())
		return spells;
	for (spell = t_spell(0); spell < k_spell_count; enum_incr(spell))
	{
		if (spells[spell])
			break;
	}
	lowest = m_spell_count[spell];

	for (; spell < k_spell_count; enum_incr(spell))
	{
		if (!spells[spell])
			continue;
		if (lowest > m_spell_count[spell])
			lowest = m_spell_count[spell];
	}
	for (spell = t_spell(0); spell < k_spell_count; enum_incr(spell))
	{
		if (!spells[spell])
			continue;
		if (m_spell_count[spell] > lowest)
			continue;
		result.set( spell );
	}
	return result;
}


// --------------------------------------------------------
// resume a turn from a saved game.
// --------------------------------------------------------
void t_adventure_map::add_defeated_hero( t_hero* hero )
{
	t_counted_ptr< t_hero > hero_ptr = hero;
	if ( std::find(m_defeated_heroes.begin(), m_defeated_heroes.end(), hero_ptr) == m_defeated_heroes.end() )
		m_defeated_heroes.push_back( hero_ptr );
	else
	{
		assert(false);
	}
}		


// --------------------------------------------------------
// Store a new final score for this or a previous map.
// --------------------------------------------------------
void t_adventure_map::store_map_score( int score, int days )
{
	assert( is_multi_scenario() );

	if ( m_carry_out_data == 0 )
		m_carry_out_data = new t_carryover_data;

	assert( m_carry_out_data->get_scenario_score_count() == get_map_number() );

	m_carry_out_data->add_scenario_score( score, days );
}
